Project

General

Profile

1
/**
2
 *  '$RCSfile$'
3
 *    Purpose: A Class that implements replication for metacat
4
 *  Copyright: 2000 Regents of the University of California and the
5
 *             National Center for Ecological Analysis and Synthesis
6
 *    Authors: Chad Berkley
7
 *
8
 *   '$Author: tao $'
9
 *     '$Date: 2016-03-23 15:57:25 -0700 (Wed, 23 Mar 2016) $'
10
 * '$Revision: 9583 $'
11
 *
12
 * This program is free software; you can redistribute it and/or modify
13
 * it under the terms of the GNU General Public License as published by
14
 * the Free Software Foundation; either version 2 of the License, or
15
 * (at your option) any later version.
16
 *
17
 * This program is distributed in the hope that it will be useful,
18
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20
 * GNU General Public License for more details.
21
 *
22
 * You should have received a copy of the GNU General Public License
23
 * along with this program; if not, write to the Free Software
24
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
25
 */
26

    
27
package edu.ucsb.nceas.metacat.replication;
28

    
29
import java.io.ByteArrayInputStream;
30
import java.io.ByteArrayOutputStream;
31
import java.io.File;
32
import java.io.FileInputStream;
33
import java.io.FileNotFoundException;
34
import java.io.FileOutputStream;
35
import java.io.IOException;
36
import java.io.InputStream;
37
import java.io.OutputStream;
38
import java.io.StringReader;
39
import java.io.Writer;
40
import java.net.MalformedURLException;
41
import java.net.URL;
42
import java.security.PrivateKey;
43
import java.security.cert.X509Certificate;
44
import java.sql.PreparedStatement;
45
import java.sql.ResultSet;
46
import java.sql.SQLException;
47
import java.sql.Timestamp;
48
import java.util.ArrayList;
49
import java.util.Arrays;
50
import java.util.Calendar;
51
import java.util.Date;
52
import java.util.HashMap;
53
import java.util.Hashtable;
54
import java.util.List;
55
import java.util.Map;
56
import java.util.Timer;
57
import java.util.Vector;
58

    
59
import javax.servlet.http.HttpServletRequest;
60
import javax.servlet.http.HttpServletResponse;
61

    
62
import org.apache.commons.io.IOUtils;
63
import org.apache.commons.lang.exception.ExceptionUtils;
64
import org.apache.http.HttpEntity;
65
import org.apache.http.HttpResponse;
66
import org.apache.http.StatusLine;
67
import org.apache.http.client.ClientProtocolException;
68
import org.apache.http.client.HttpClient;
69
import org.apache.http.client.HttpResponseException;
70
import org.apache.http.impl.client.HttpClientBuilder;
71
import org.apache.http.impl.client.HttpClients;
72
import org.apache.http.client.config.RequestConfig;
73
import org.apache.log4j.Logger;
74
import org.dataone.client.auth.CertificateManager;
75
import org.dataone.client.rest.RestClient;
76
import org.dataone.client.utils.HttpUtils;
77
import org.dataone.service.types.v1.Identifier;
78
import org.dataone.service.types.v2.SystemMetadata;
79
import org.dataone.service.util.DateTimeMarshaller;
80
import org.dataone.service.util.TypeMarshaller;
81
import org.jibx.runtime.JiBXException;
82
import org.xml.sax.InputSource;
83
import org.xml.sax.SAXException;
84
import org.xml.sax.XMLReader;
85

    
86
import edu.ucsb.nceas.metacat.DocumentImpl;
87
import edu.ucsb.nceas.metacat.DocumentImplWrapper;
88
import edu.ucsb.nceas.metacat.EventLog;
89
import edu.ucsb.nceas.metacat.IdentifierManager;
90
import edu.ucsb.nceas.metacat.McdbDocNotFoundException;
91
import edu.ucsb.nceas.metacat.McdbException;
92
import edu.ucsb.nceas.metacat.accesscontrol.AccessControlException;
93
import edu.ucsb.nceas.metacat.accesscontrol.AccessControlForSingleFile;
94
import edu.ucsb.nceas.metacat.accesscontrol.PermOrderException;
95
import edu.ucsb.nceas.metacat.admin.upgrade.RemoveInvalidReplicas;
96
import edu.ucsb.nceas.metacat.admin.upgrade.UpdateDOI;
97
import edu.ucsb.nceas.metacat.admin.upgrade.dataone.GenerateORE;
98
import edu.ucsb.nceas.metacat.admin.upgrade.dataone.GenerateSystemMetadata;
99
import edu.ucsb.nceas.metacat.client.InsufficientKarmaException;
100
import edu.ucsb.nceas.metacat.database.DBConnection;
101
import edu.ucsb.nceas.metacat.database.DBConnectionPool;
102
import edu.ucsb.nceas.metacat.database.DatabaseService;
103
import edu.ucsb.nceas.metacat.dataone.SyncAccessPolicy;
104
import edu.ucsb.nceas.metacat.dataone.hazelcast.HazelcastService;
105
import edu.ucsb.nceas.metacat.index.MetacatSolrIndex;
106
import edu.ucsb.nceas.metacat.properties.PropertyService;
107
import edu.ucsb.nceas.metacat.shared.BaseService;
108
import edu.ucsb.nceas.metacat.shared.HandlerException;
109
import edu.ucsb.nceas.metacat.shared.ServiceException;
110
import edu.ucsb.nceas.metacat.util.DocumentUtil;
111
import edu.ucsb.nceas.metacat.util.MetacatUtil;
112
import edu.ucsb.nceas.metacat.util.ReplicationUtil;
113
import edu.ucsb.nceas.metacat.util.SystemUtil;
114
import edu.ucsb.nceas.utilities.FileUtil;
115
import edu.ucsb.nceas.utilities.GeneralPropertyException;
116
import edu.ucsb.nceas.utilities.PropertyNotFoundException;
117
import edu.ucsb.nceas.utilities.access.DocInfoHandler;
118
import edu.ucsb.nceas.utilities.access.XMLAccessDAO;
119

    
120
public class ReplicationService extends BaseService {
121

    
122
	private static ReplicationService replicationService = null;
123

    
124
	private long timeInterval;
125
	private Date firstTime;
126
	private boolean timedReplicationIsOn = false;
127
	Timer replicationDaemon;
128
	private static Vector<String> fileLocks = new Vector<String>();
129
//	private Thread lockThread = null;
130
	public static final String FORCEREPLICATEDELETE = "forcereplicatedelete";
131
	public static final String FORCEREPLICATEDELETEALL = "forcereplicatedeleteall";
132
	private static String TIMEREPLICATION = "replication.timedreplication";
133
	private static String TIMEREPLICATIONINTERVAl ="replication.timedreplicationinterval";
134
	private static String FIRSTTIME = "replication.firsttimedreplication";
135
	private static final int TIMEINTERVALLIMIT = 7200000;
136
	public static final String REPLICATIONUSER = "replication";
137
	
138
	//private static RestClient sslClient = null;
139
	private static int CLIENTTIMEOUT = 30000;
140

    
141
	public static final String REPLICATION_LOG_FILE_NAME = "metacatreplication.log";
142

    
143
	private static String DATA_FILE_FLAG = null;
144
	public static String METACAT_REPL_ERROR_MSG = null;
145
	private static Logger logReplication = Logger.getLogger("ReplicationLogging");
146
	private static Logger logMetacat = Logger.getLogger(ReplicationService.class);
147

    
148
	static {
149
		// lookup the client timeout
150
		String clientTimeout = null;
151
		try {
152
			clientTimeout = PropertyService.getProperty("replication.client.timeout");
153
			CLIENTTIMEOUT = Integer.parseInt(clientTimeout);
154
		} catch (Exception e) {
155
			// just use default
156
			logReplication.warn("No custom client timeout specified in configuration, using default." + e.getMessage());
157
		}
158
		try {
159
			DATA_FILE_FLAG = PropertyService.getProperty("replication.datafileflag");
160
		} catch (PropertyNotFoundException e) {
161
			logReplication.error("No 'replication.datafileflag' specified in configuration." + e.getMessage());
162
		}
163

    
164
	}
165
	
166
	private ReplicationService() throws ServiceException {
167
		_serviceName = "ReplicationService";
168
		
169
		initialize();
170
	}
171
	
172
	private void initialize() throws ServiceException {
173
				
174
		// initialize db connections to handle any update requests
175
		// deltaT = util.getProperty("replication.deltaT");
176
		// the default deltaT can be set from metacat.properties
177
		// create a thread to do the delta-T check but don't execute it yet
178
		replicationDaemon = new Timer(true);
179
		try {
180
			String replLogFile = PropertyService.getProperty("replication.logdir")
181
				+ FileUtil.getFS() + REPLICATION_LOG_FILE_NAME;
182
			METACAT_REPL_ERROR_MSG = "An error occurred in replication.  Please see the " +
183
				"replication log at: " + replLogFile;
184
			
185
			String timedRepIsOnStr = 
186
				PropertyService.getProperty("replication.timedreplication");
187
			timedReplicationIsOn = (new Boolean(timedRepIsOnStr)).booleanValue();
188
			logReplication.info("ReplicationService.initialize - The timed replication on is" + timedReplicationIsOn);
189

    
190
			String timeIntervalStr = 
191
				PropertyService.getProperty("replication.timedreplicationinterval");
192
			timeInterval = (new Long(timeIntervalStr)).longValue();
193
			logReplication.info("ReplicationService.initialize - The timed replication time Interval is " + timeInterval);
194

    
195
			String firstTimeStr = 
196
				PropertyService.getProperty("replication.firsttimedreplication");
197
			logReplication.info("ReplicationService.initialize - first replication time form property is " + firstTimeStr);
198
			firstTime = ReplicationHandler.combinateCurrentDateAndGivenTime(firstTimeStr);
199

    
200
			logReplication.info("ReplicationService.initialize - After combine current time, the real first time is "
201
					+ firstTime.toString() + " minisec");
202

    
203
			// set up time replication if it is on
204
			if (timedReplicationIsOn) {
205
				replicationDaemon.scheduleAtFixedRate(new ReplicationHandler(),
206
						firstTime, timeInterval);
207
				logReplication.info("ReplicationService.initialize - deltaT handler started with rate="
208
						+ timeInterval + " mini seconds at " + firstTime.toString());
209
			}
210

    
211
		} catch (PropertyNotFoundException pnfe) {
212
			throw new ServiceException(
213
					"ReplicationService.initialize - Property error while instantiating "
214
							+ "replication service: " + pnfe.getMessage());
215
		} catch (HandlerException he) {
216
			throw new ServiceException(
217
					"ReplicationService.initialize - Handler error while instantiating "
218
							+ "replication service" + he.getMessage());
219
		} 
220
	}
221

    
222
	/**
223
	 * Get the single instance of SessionService.
224
	 * 
225
	 * @return the single instance of SessionService
226
	 */
227
	public static ReplicationService getInstance() throws ServiceException {
228
		if (replicationService == null) {
229
			replicationService = new ReplicationService();
230
		}
231
		return replicationService;
232
	}
233

    
234
	public boolean refreshable() {
235
		return true;
236
	}
237

    
238
	protected void doRefresh() throws ServiceException {
239
		return;
240
	}
241
	
242
	public void stop() throws ServiceException{
243
		
244
	}
245

    
246
	public void stopReplication() throws ServiceException {
247
	      //stop the replication server
248
	      replicationDaemon.cancel();
249
	      replicationDaemon = new Timer(true);
250
	      timedReplicationIsOn = false;
251
	      try {
252
	    	  PropertyService.setProperty("replication.timedreplication", (new Boolean(timedReplicationIsOn)).toString());
253
	      } catch (GeneralPropertyException gpe) {
254
	    	  logReplication.warn("ReplicationService.stopReplication - Could not set replication.timedreplication property: " + gpe.getMessage());
255
	      }
256

    
257
	      logReplication.info("ReplicationService.stopReplication - deltaT handler stopped");
258
		return;
259
	}
260
	
261
	public void startReplication(Hashtable<String, String[]> params) throws ServiceException {
262

    
263
	       String firstTimeStr = "";
264
	      //start the replication server
265
	       if ( params.containsKey("rate") ) {
266
	        timeInterval = new Long(
267
	               new String(((String[])params.get("rate"))[0])).longValue();
268
	        if(timeInterval < TIMEINTERVALLIMIT) {
269
	            //deltaT<30 is a timing mess!
270
	            timeInterval = TIMEINTERVALLIMIT;
271
	            throw new ServiceException("Replication deltaT rate cannot be less than "+
272
	                    TIMEINTERVALLIMIT + " millisecs and system automatically setup the rate to "+TIMEINTERVALLIMIT);
273
	        }
274
	      } else {
275
	        timeInterval = TIMEINTERVALLIMIT ;
276
	      }
277
	      logReplication.info("ReplicationService.startReplication - New rate is: " + timeInterval + " mini seconds.");
278
	      if ( params.containsKey("firsttime"))
279
	      {
280
	         firstTimeStr = ((String[])params.get("firsttime"))[0];
281
	         try
282
	         {
283
	           firstTime = ReplicationHandler.combinateCurrentDateAndGivenTime(firstTimeStr);
284
	           logReplication.info("ReplicationService.startReplication - The first time setting is "+firstTime.toString());
285
	         }
286
	         catch (HandlerException e)
287
	         {
288
	            throw new ServiceException(e.getMessage());
289
	         }
290
	         logReplication.warn("After combine current time, the real first time is "
291
	                                  +firstTime.toString()+" minisec");
292
	      }
293
	      else
294
	      {
295
	    	  logMetacat.error("ReplicationService.startReplication - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
296
	          logReplication.error("ReplicationService.startReplication - You should specify the first time " +
297
	                                  "to start a time replication");
298
	          return;
299
	      }
300
	      
301
	      timedReplicationIsOn = true;
302
	      try {
303
	      // save settings to property file
304
	      PropertyService.setProperty(TIMEREPLICATION, (new Boolean(timedReplicationIsOn)).toString());
305
	      // note we couldn't use firstTime object because it has date info
306
	      // we only need time info such as 10:00 PM
307
	      PropertyService.setProperty(FIRSTTIME, firstTimeStr);
308
	      PropertyService.setProperty(TIMEREPLICATIONINTERVAl, (new Long(timeInterval)).toString());
309
	      } catch (GeneralPropertyException gpe) {
310
	    	  logReplication.warn("ReplicationService.startReplication - Could not set property: " + gpe.getMessage());
311
	      }
312
	      replicationDaemon.cancel();
313
	      replicationDaemon = new Timer(true);
314
	      replicationDaemon.scheduleAtFixedRate(new ReplicationHandler(), firstTime,
315
	                                            timeInterval);
316
	      
317
	      logReplication.info("ReplicationService.startReplication - deltaT handler started with rate=" +
318
	                                    timeInterval + " milliseconds at " +firstTime.toString());
319

    
320
	}
321
	
322
	public void runOnce() throws ServiceException {
323
	      //updates this server exactly once
324
	      replicationDaemon.schedule(new ReplicationHandler(), 0);
325
	}
326
	
327
	/**
328
	 * This method can add, delete and list the servers currently included in
329
	 * xml_replication.
330
	 * action           subaction            other needed params
331
	 * ---------------------------------------------------------
332
	 * servercontrol    add                  server
333
	 * servercontrol    delete               server
334
	 * servercontrol    list
335
	 */
336
	public static void handleServerControlRequest(
337
			Hashtable<String, String[]> params, HttpServletRequest request, HttpServletResponse response) {
338
		String subaction = ((String[]) params.get("subaction"))[0];
339
		DBConnection dbConn = null;
340
		int serialNumber = -1;
341
		PreparedStatement pstmt = null;
342
		String replicate = null;
343
		String server = null;
344
		String dataReplicate = null;
345
		String hub = null;
346
		Writer out = null;
347
		boolean showGenerateSystemMetadata = true;
348
		try {
349
			response.setContentType("text/xml");
350
			out = response.getWriter();
351
			
352
			//conn = util.openDBConnection();
353
			dbConn = DBConnectionPool
354
					.getDBConnection("MetacatReplication.handleServerControlRequest");
355
			serialNumber = dbConn.getCheckOutSerialNumber();
356

    
357
			// add server to server list
358
			if (subaction.equals("add")) {
359
				replicate = ((String[]) params.get("replicate"))[0];
360
				server = ((String[]) params.get("server"))[0];
361

    
362
				//Get data replication value
363
				dataReplicate = ((String[]) params.get("datareplicate"))[0];
364
				
365
				//Get hub value
366
				hub = ((String[]) params.get("hub"))[0];
367

    
368
				Calendar cal = Calendar.getInstance();
369
                cal.set(1980, 1, 1);
370
				String sql = "INSERT INTO xml_replication "
371
						+ "(server, last_checked, replicate, datareplicate, hub) "
372
						+ "VALUES (?,?,?,?,?)";
373
				
374
				pstmt = dbConn.prepareStatement(sql);
375
						
376
				pstmt.setString(1, server);
377
				pstmt.setTimestamp(2, new Timestamp(cal.getTimeInMillis()));
378
				pstmt.setInt(3, Integer.parseInt(replicate));
379
				pstmt.setInt(4, Integer.parseInt(dataReplicate));
380
				pstmt.setInt(5, Integer.parseInt(hub));
381
				
382
				String sqlReport = "XMLAccessAccess.getXMLAccessForDoc - SQL: " + sql;
383
				sqlReport += " [" + server + "," + replicate + 
384
					"," + dataReplicate + "," + hub + "]";
385
				
386
				logMetacat.info(sqlReport);
387
				
388
				pstmt.execute();
389
				pstmt.close();
390
				dbConn.commit();
391
				out.write("Server " + server + " added");
392
				
393
				
394
				// delete server from server list
395
			} else if (subaction.equals("delete")) {
396
				server = ((String[]) params.get("server"))[0];
397
				pstmt = dbConn.prepareStatement("DELETE FROM xml_replication "
398
						+ "WHERE server LIKE ?");
399
				pstmt.setString(1, server);
400
				pstmt.execute();
401
				pstmt.close();
402
				dbConn.commit();
403
				out.write("Server " + server + " deleted");
404
			} else if (subaction.equals("list")) {
405
				// nothing special - it's the default behavior
406

    
407
			} else if (subaction.equals("generatesystemmetadata")) {
408
				GenerateSystemMetadata gsm = new GenerateSystemMetadata();
409
				int serverLocation = -1;
410
				String serverid = ((String[]) params.get("serverid"))[0];
411
				serverLocation = Integer.parseInt(serverid);
412
				gsm.setServerLocation(serverLocation );
413
				gsm.multiThreadUpgrade();
414
				out.write("System Metadata generated for server " + serverid);
415
				
416
			} else if (subaction.equals("generateore")) {
417
				GenerateORE gore = new GenerateORE();
418
				int serverLocation = -1;
419
				String serverid = ((String[]) params.get("serverid"))[0];
420
				serverLocation = Integer.parseInt(serverid);
421
				gore.setServerLocation(serverLocation );
422
				gore.upgrade();
423
				out.write("Generated ORE maps for server " + serverid);
424
				
425
			} else if (subaction.equals("updatedoi")) {
426
				UpdateDOI udoi = new UpdateDOI();
427
				int serverLocation = -1;
428
				String serverid = ((String[]) params.get("serverid"))[0];
429
				serverLocation = Integer.parseInt(serverid);
430
				udoi.setServerLocation(serverLocation );
431
								
432
				//Get the list of IDs, if any were given
433
				String ids = ((String[]) params.get("ids"))[0];
434
				
435
				//Get the formatId, if one was given
436
				String formatIds = ((String[]) params.get("formatIds"))[0];
437
				
438
				//Allow DOI's to be updated by both ID and formatId
439
				if((ids.length() > 0) || (formatIds.length() > 0)){
440
					//If at least one ID was given, update their DOI registrations
441
					if(ids.length() > 0){
442
						String delimeter = " "; 
443
						String[] idArray = ids.split(delimeter);	
444
						List<String> idList = Arrays.asList(idArray);
445
						udoi.upgradeById(idList);
446
					}
447
					
448
					//If at least one formatId was given, update the DOI registrations
449
					if(formatIds.length() > 0){
450
						String delimeter = " "; 
451
						String[] formatIdArray = formatIds.split(delimeter);	
452
						List<String> formatIdList = Arrays.asList(formatIdArray);
453
						udoi.upgradeByFormatId(formatIdList);
454
					}
455
				}
456
				else{
457
					udoi.upgrade();
458
				}
459
				out.write("Updated DOI's for server " + serverid);
460
				
461
			} else if (subaction.equals("removeinvalidreplicas")) {
462
				RemoveInvalidReplicas rir = new RemoveInvalidReplicas();
463
				int serverLocation = -1;
464
				String serverid = ((String[]) params.get("serverid"))[0];
465
				serverLocation = Integer.parseInt(serverid);
466
				rir.setServerLocation(serverLocation );
467
				rir.upgrade();
468
				out.write("Removed invalid replicas for server " + serverid);
469
				
470
			} else if (subaction.equals("syncaccesspolicy")) {
471
				SyncAccessPolicy syncAP = new SyncAccessPolicy();
472
				response.setContentType("text/html");
473
				out = response.getWriter();
474
				if (params.containsKey("pid")) {
475
					String[] pids = params.get("pid");
476
					logMetacat.debug("Attempting to sync access policies for pids: " + pids);
477
					ArrayList<String> pidsToSync = new ArrayList<String>(Arrays.asList(pids));
478
					try {
479
						List<Identifier> syncedPids = syncAP.sync(pidsToSync);
480
						out.write("<html><body>Syncing access policies has completed for " + syncedPids.size() + " pids.</body></html>");
481
					} catch (Exception e) {
482
						logMetacat.error("Error syncing all access polies: "
483
								+ e.getMessage());
484
						response.setContentType("text/html");
485
						out = response.getWriter();
486
						out.write("<html><body>Error syncing access policies</body></html>");
487
					}
488
				} else {
489
					logMetacat.debug("Request to sync all access policies has been submitted.");
490
					try {
491
						syncAP.syncAll();
492
						out.write("<html><body>Request to sync all access policies has been submitted.</body></html>");
493
					} catch (Exception e) {
494
						logMetacat.error("Error syncing access policies: "
495
								+ e.getMessage());
496
						out.write("<html><body>Error syncing access policies: " + e.getMessage() + " </body></html>");
497
					}
498
				}
499
			} else {
500
			
501
				out.write("<error>Unkonwn subaction</error>");
502
				return;
503
			}
504
			
505
			// hide SM/ORE buttons?
506
//			String dataoneConfigured = PropertyService.getProperty("configutil.dataoneConfigured");
507
//			if (dataoneConfigured != null) {
508
//				showGenerateSystemMetadata = Boolean.parseBoolean(dataoneConfigured);
509
//			}
510
			
511
			// always list them after processing
512
			response.setContentType("text/html");
513
			out.write("<html><body><table border=\"1\">");
514
			out.write("<tr><td><b>server</b></td>");
515
			out.write("<td><b>last_checked</b></td>");
516
			out.write("<td><b>replicate</b></td>");
517
			out.write("<td><b>datareplicate</b></td>");
518
			out.write("<td><b>hub</b></td>");
519
			if (showGenerateSystemMetadata) {
520
				out.write("<td><b>System Metadata</b></td>");
521
				out.write("<td><b>ORE Maps</b></td>");
522
				out.write("<td><b>DOI Registrations</b></td>");
523
				out.write("<td><b>Invalid Replicas</b></td>");
524
			}
525
			out.write("<td><b>Sync Access Policies</b></td>");
526
			out.write("</tr>");
527

    
528
			pstmt = dbConn.prepareStatement("SELECT serverid, server, last_checked, replicate, datareplicate, hub FROM xml_replication");
529
			pstmt.execute();
530
			ResultSet rs = pstmt.getResultSet();
531
			boolean tablehasrows = rs.next();
532
			while (tablehasrows) {
533
				String serverId = rs.getString(1);
534
				out.write("<tr><td>" + rs.getString(2) + "</td><td>");
535
				out.write(rs.getString(3) + "</td><td>");
536
				out.write(rs.getString(4) + "</td><td>");
537
				out.write(rs.getString(5) + "</td><td>");
538
				out.write(rs.getString(6) + "</td>");
539
				if (showGenerateSystemMetadata) {
540
					// for SM
541
					out.write("<td><form action='" + request.getContextPath() + "/admin'>");
542
					out.write("<input name='serverid' type='hidden' value='" + serverId  + "'/>");
543
					out.write("<input name='configureType' type='hidden' value='replication'/>");
544
					out.write("<input name='action' type='hidden' value='servercontrol'/>");
545
					out.write("<input name='subaction' type='hidden' value='generatesystemmetadata'/>");
546
					out.write("<input type='submit' value='Generate System Metadata'/>");
547
					out.write("</form></td>");
548
					
549
					// for ORE maps
550
					out.write("<td><form action='" + request.getContextPath() + "/admin'>");
551
					out.write("<input name='serverid' type='hidden' value='" + serverId + "'/>");
552
					out.write("<input name='configureType' type='hidden' value='replication'/>");
553
					out.write("<input name='action' type='hidden' value='servercontrol'/>");
554
					out.write("<input name='subaction' type='hidden' value='generateore'/>");
555
					out.write("<input type='submit' value='Generate ORE'/>");
556
					out.write("</form></td>");
557
					
558
					// for DOI updating
559
					out.write("<td><form action='" + request.getContextPath() + "/admin'>");
560
					out.write("<input name='serverid' type='hidden' value='" + serverId + "'/>");
561
					out.write("<input name='configureType' type='hidden' value='replication'/>");
562
					out.write("<input name='action' type='hidden' value='servercontrol'/>");
563
					out.write("<input name='subaction' type='hidden' value='updatedoi'/>");
564
					out.write("<label>Update by ID:</label>");
565
					out.write("<textarea name='ids'></textarea>");
566
					out.write("<label>Update by formatId:</label>");
567
					out.write("<textarea name='formatIds'></textarea>");
568
					out.write("<input type='submit' value='Update DOIs'/>");
569
					out.write("</form></td>");
570
					
571
					// for invalid replicas
572
					out.write("<td><form action='" + request.getContextPath() + "/admin'>");
573
					out.write("<input name='serverid' type='hidden' value='" + serverId + "'/>");
574
					out.write("<input name='configureType' type='hidden' value='replication'/>");
575
					out.write("<input name='action' type='hidden' value='servercontrol'/>");
576
					out.write("<input name='subaction' type='hidden' value='removeinvalidreplicas'/>");
577
					String disabled = "";
578
					if (Integer.valueOf(serverId) == 1) {
579
						disabled = "disabled='true'";
580
					}
581
					out.write("<input type='submit' value='Remove Invalid Replicas' " + disabled + " />");
582
					out.write("</form></td>");
583
				}
584
				// for syncing access policies (MN -> CN)
585
				out.write("<td><form action='" + request.getContextPath() + "/admin'>");
586
				out.write("<input name='serverid' type='hidden' value='" + serverId + "'/>");
587
				out.write("<input name='configureType' type='hidden' value='replication'/>");
588
				out.write("<input name='action' type='hidden' value='servercontrol'/>");
589
				out.write("<input name='subaction' type='hidden' value='syncaccesspolicy'/>");
590
				out.write("<input type='submit' value='Sync access policies'/>");
591
				out.write("</form></td>");
592
				
593
				out.write("</tr>");
594

    
595
				tablehasrows = rs.next();
596
			}
597
			out.write("</table></body></html>");
598
			
599
			
600
			pstmt.close();
601
			//conn.close();
602

    
603
		} catch (Exception e) {
604
			logMetacat.error("ReplicationService.handleServerControlRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
605
			logReplication.error("ReplicationService.handleServerControlRequest - Error in "
606
					+ "MetacatReplication.handleServerControlRequest " + e.getMessage());
607
			e.printStackTrace(System.out);
608
		} finally {
609
			try {
610
				pstmt.close();
611
			}//try
612
			catch (SQLException ee) {
613
				logMetacat.error("ReplicationService.handleServerControlRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
614
				logReplication.error("ReplicationService.handleServerControlRequest - Error in MetacatReplication.handleServerControlRequest to close pstmt "
615
						+ ee.getMessage());
616
			}//catch
617
			finally {
618
				DBConnectionPool.returnDBConnection(dbConn, serialNumber);
619
			}//finally
620
			if (out != null) {
621
				try {
622
					out.close();
623
				} catch (IOException e) {
624
					logMetacat.error(e.getMessage(), e);
625
				}
626
			}
627
		}//finally
628

    
629
	}
630

    
631
	/**
632
	 * when a forcereplication request comes in, local host sends a read request
633
	 * to the requesting server (remote server) for the specified docid. Then
634
	 * store it in local database.
635
	 */
636
	protected static void handleForceReplicateRequest(
637
			Hashtable<String, String[]> params, HttpServletResponse response,
638
			HttpServletRequest request) {
639
		String server = ((String[]) params.get("server"))[0]; // the server that
640
		String docid = ((String[]) params.get("docid"))[0]; // sent the document
641
		String dbaction = "UPDATE"; // the default action is UPDATE
642
		//    boolean override = false;
643
		//    int serverCode = 1;
644
		DBConnection dbConn = null;
645
		int serialNumber = -1;
646
		String docName = null;
647

    
648
		try {
649
			//if the url contains a dbaction then the default action is overridden
650
			if (params.containsKey("dbaction")) {
651
				dbaction = ((String[]) params.get("dbaction"))[0];
652
				//serverCode = MetacatReplication.getServerCode(server);
653
				//override = true; //we are now overriding the default action
654
			}
655
			logReplication.info("ReplicationService.handleForceReplicateRequest - Force replication request from: " + server);
656
			logReplication.info("ReplicationService.handleForceReplicateRequest - Force replication docid: " + docid);
657
			logReplication.info("ReplicationService.handleForceReplicateRequest - Force replication action: " + dbaction);
658
			// sending back read request to remote server
659
			URL u = new URL("https://" + server + "?server="
660
					+ MetacatUtil.getLocalReplicationServerName() + "&action=read&docid="
661
					+ docid);
662
			byte[] xmlBytes = ReplicationService.getURLBytes(u);
663
            String xmldoc = new String(xmlBytes, "UTF-8");
664

    
665
			// get the document info from server
666
			URL docinfourl = new URL("https://" + server + "?server="
667
					+ MetacatUtil.getLocalReplicationServerName()
668
					+ "&action=getdocumentinfo&docid=" + docid);
669
			
670

    
671
			String docInfoStr = ReplicationService.getURLContent(docinfourl);
672
			// strip out the system metadata portion
673
			String systemMetadataXML = ReplicationUtil.getSystemMetadataContent(docInfoStr);
674
			docInfoStr = ReplicationUtil.getContentWithoutSystemMetadata(docInfoStr);
675
		   	  
676
			//dih is the parser for the docinfo xml format
677
			DocInfoHandler dih = new DocInfoHandler();
678
			XMLReader docinfoParser = ReplicationHandler.initParser(dih);
679
			docinfoParser.parse(new InputSource(new StringReader(docInfoStr)));
680
			//      Hashtable<String,Vector<AccessControlForSingleFile>> docinfoHash = dih.getDocInfo();
681
			Hashtable<String, String> docinfoHash = dih.getDocInfo();
682
			
683
			// Get home server of this docid
684
			String homeServer = (String) docinfoHash.get("home_server");
685
			
686
			// process system metadata
687
			SystemMetadata sysMeta = null;
688
			if (systemMetadataXML != null) {
689
				sysMeta = 
690
					TypeMarshaller.unmarshalTypeFromStream(
691
							SystemMetadata.class,
692
							new ByteArrayInputStream(systemMetadataXML.getBytes("UTF-8")));
693
				// need the guid-to-docid mapping
694
				boolean mappingExists = true;
695
		      	mappingExists = IdentifierManager.getInstance().mappingExists(sysMeta.getIdentifier().getValue());
696
		      	if (!mappingExists) {
697
		      		IdentifierManager.getInstance().createMapping(sysMeta.getIdentifier().getValue(), docid);
698
		      	}
699
				// save the system metadata
700
				HazelcastService.getInstance().getSystemMetadataMap().put(sysMeta.getIdentifier(), sysMeta);
701
				
702
			}
703
      
704
			// dates
705
			String createdDateString = docinfoHash.get("date_created");
706
			String updatedDateString = docinfoHash.get("date_updated");
707
			Date createdDate = DateTimeMarshaller.deserializeDateToUTC(createdDateString);
708
			Date updatedDate = DateTimeMarshaller.deserializeDateToUTC(updatedDateString);
709
		      
710
			logReplication.info("ReplicationService.handleForceReplicateRequest - homeServer: " + homeServer);
711
			// Get Document type
712
			String docType = (String) docinfoHash.get("doctype");
713
			logReplication.info("ReplicationService.handleForceReplicateRequest - docType: " + docType);
714
			String parserBase = null;
715
			// this for eml2 and we need user eml2 parser
716
			if (docType != null
717
					&& (docType.trim()).equals(DocumentImpl.EML2_0_0NAMESPACE)) {
718
				logReplication.warn("ReplicationService.handleForceReplicateRequest - This is an eml200 document!");
719
				parserBase = DocumentImpl.EML200;
720
			} else if (docType != null
721
					&& (docType.trim()).equals(DocumentImpl.EML2_0_1NAMESPACE)) {
722
				logReplication.warn("ReplicationService.handleForceReplicateRequest - This is an eml2.0.1 document!");
723
				parserBase = DocumentImpl.EML200;
724
			} else if (docType != null
725
					&& (docType.trim()).equals(DocumentImpl.EML2_1_0NAMESPACE)) {
726
				logReplication.warn("ReplicationService.handleForceReplicateRequest - This is an eml2.1.0 document!");
727
				parserBase = DocumentImpl.EML210;
728
			} else if (docType != null
729
					&& (docType.trim()).equals(DocumentImpl.EML2_1_1NAMESPACE)) {
730
				logReplication.warn("ReplicationService.handleForceReplicateRequest - This is an eml2.1.1 document!");
731
				parserBase = DocumentImpl.EML210;
732
			}
733
			logReplication.warn("ReplicationService.handleForceReplicateRequest - The parserBase is: " + parserBase);
734
			
735
			String formatId = null;
736
		    //get the format id from the system metadata 
737
		    if(sysMeta != null && sysMeta.getFormatId() != null) {
738
		          logMetacat.debug("ReplicationService.handleForceReplicateRequest - the format id will be got from the system metadata for the object "+docid);
739
		          formatId = sysMeta.getFormatId().getValue();
740
		    }
741
		      
742
			// Get DBConnection from pool
743
			dbConn = DBConnectionPool
744
					.getDBConnection("MetacatReplication.handleForceReplicateRequest");
745
			serialNumber = dbConn.getCheckOutSerialNumber();
746
			// write the document to local database
747
			DocumentImplWrapper wrapper = new DocumentImplWrapper(parserBase, false, false);
748
			//try this independently so we can set access even if the update action is invalid (i.e docid has not changed)
749
			try {
750
				wrapper.writeReplication(dbConn, xmldoc, xmlBytes, null, null,
751
						dbaction, docid, null, null, homeServer, server, createdDate,
752
						updatedDate, formatId);
753
			} finally {
754
				if(sysMeta != null) {
755
					// submit for indexing. When the doc writing process fails, the index process will fail as well. But this failure
756
					// will not interrupt the process.
757
					try {
758
						MetacatSolrIndex.getInstance().submit(sysMeta.getIdentifier(), sysMeta, null, true);
759
					} catch (Exception ee) {
760
						logReplication.warn("ReplicationService.handleForceReplicateRequest - couldn't index the doc since "+ee.getMessage());
761
					}
762
	                
763
				}
764
				//process extra access rules before dealing with the write exception (doc exist already)
765
				try {
766
		        	// check if we had a guid -> docid mapping
767
		        	String docidNoRev = DocumentUtil.getDocIdFromAccessionNumber(docid);
768
		        	int rev = DocumentUtil.getRevisionFromAccessionNumber(docid);
769
		        	IdentifierManager.getInstance().getGUID(docidNoRev, rev);
770
		        	// no need to create the mapping if we have it
771
		        } catch (McdbDocNotFoundException mcdbe) {
772
		        	// create mapping if we don't
773
		        	IdentifierManager.getInstance().createMapping(docid, docid);
774
		        }
775
		        Vector<XMLAccessDAO> accessControlList = dih.getAccessControlList();
776
		        if (accessControlList != null) {
777
		        	AccessControlForSingleFile acfsf = new AccessControlForSingleFile(docid);
778
		        	for (XMLAccessDAO xmlAccessDAO : accessControlList) {
779
		        		try {
780
			        		if (!acfsf.accessControlExists(xmlAccessDAO)) {
781
			        			acfsf.insertPermissions(xmlAccessDAO);
782
								logReplication.info("ReplicationService.handleForceReplicateRequest - document " + docid
783
										+ " permissions added to DB");
784
			        		}
785
		        		} catch (PermOrderException poe) {
786
		        			// this is problematic, but should not prevent us from replicating
787
		        			// see https://redmine.dataone.org/issues/2583
788
		        			String msg = "Could not insert access control for: " + docid + " Message: " + poe.getMessage();
789
		        			logMetacat.error(msg, poe);
790
		        			logReplication.error(msg, poe);
791
		        		}
792
		            }
793
		        }
794
		        
795
		        // process the real owner and updater
796
				String user = (String) docinfoHash.get("user_owner");
797
				String updated = (String) docinfoHash.get("user_updated");
798
		        updateUserOwner(dbConn, docid, user, updated);
799

    
800
				logReplication.info("ReplicationService.handleForceReplicateRequest - document " + docid + " added to DB with "
801
						+ "action " + dbaction);
802
				
803
				EventLog.getInstance().log(request.getRemoteAddr(), request.getHeader("User-Agent"), REPLICATIONUSER, docid, dbaction);
804
			}
805
		} catch (SQLException sqle) {
806
			logMetacat.error("ReplicationService.handleForceReplicateRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
807
			logReplication.error("ReplicationService.handleForceReplicateRequest - SQL error when adding doc " + docid + 
808
					" to DB with action " + dbaction + ": " + sqle.getMessage());
809
		} catch (MalformedURLException mue) {
810
			logMetacat.error("ReplicationService.handleForceReplicateRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
811
			logReplication.error("ReplicationService.handleForceReplicateRequest - URL error when adding doc " + docid + 
812
					" to DB with action " + dbaction + ": " + mue.getMessage());
813
		} catch (SAXException se) {
814
			logMetacat.error("ReplicationService.handleForceReplicateRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
815
			logReplication.error("ReplicationService.handleForceReplicateRequest - SAX parsing error when adding doc " + docid + 
816
					" to DB with action " + dbaction + ": " + se.getMessage());
817
		} catch (HandlerException he) {
818
			logMetacat.error("ReplicationService.handleForceReplicateRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
819
			logReplication.error("ReplicationService.handleForceReplicateRequest - Handler error when adding doc " + docid + 
820
					" to DB with action " + dbaction + ": " + he.getMessage());
821
		} catch (IOException ioe) {
822
			logMetacat.error("ReplicationService.handleForceReplicateRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
823
			logReplication.error("ReplicationService.handleForceReplicateRequest - I/O error when adding doc " + docid + 
824
					" to DB with action " + dbaction + ": " + ioe.getMessage());
825
		} catch (PermOrderException poe) {
826
			logMetacat.error("ReplicationService.handleForceReplicateRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
827
			logReplication.error("ReplicationService.handleForceReplicateRequest - Permissions order error when adding doc " + docid + 
828
					" to DB with action " + dbaction + ": " + poe.getMessage());
829
		} catch (AccessControlException ace) {
830
			logMetacat.error("ReplicationService.handleForceReplicateRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
831
			logReplication.error("ReplicationService.handleForceReplicateRequest - Permissions order error when adding doc " + docid + 
832
					" to DB with action " + dbaction + ": " + ace.getMessage());
833
		} catch (Exception e) {
834
			logMetacat.error("ReplicationService.handleForceReplicateRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
835
			logReplication.error("ReplicationService.handleForceReplicateRequest - General error when adding doc " + docid + 
836
					" to DB with action " + dbaction + ": " + e.getMessage());
837
		} finally {
838
			// Return the checked out DBConnection
839
			DBConnectionPool.returnDBConnection(dbConn, serialNumber);
840
		}//finally
841
	}
842

    
843
	/*
844
	 * when a forcereplication delete request comes in, local host will delete this
845
	 * document
846
	 */
847
	protected static void handleForceReplicateDeleteRequest(
848
			Hashtable<String, String[]> params, HttpServletResponse response,
849
			HttpServletRequest request, boolean removeAll) {
850
		String server = ((String[]) params.get("server"))[0]; // the server that
851
		String docid = ((String[]) params.get("docid"))[0]; // sent the document
852
		try {
853
			logReplication.info("ReplicationService.handleForceReplicateDeleteRequest - force replication delete request from " + server);
854
			logReplication.info("ReplicationService.handleForceReplicateDeleteRequest - force replication delete docid " + docid);
855
			logReplication.info("ReplicationService.handleForceReplicateDeleteRequest - Force replication delete request from: " + server);
856
			logReplication.info("ReplicationService.handleForceReplicateDeleteRequest - Force replication delete docid: " + docid);
857
			DocumentImpl.delete(docid, null, null, server, removeAll);
858
			logReplication.info("ReplicationService.handleForceReplicateDeleteRequest - document " + docid + " was successfully deleted ");
859
			EventLog.getInstance().log(request.getRemoteAddr(), request.getHeader("User-Agent"), REPLICATIONUSER, docid,
860
					"delete");
861
			logReplication.info("ReplicationService.handleForceReplicateDeleteRequest - document " + docid + " was successfully deleted ");
862
		} catch (McdbDocNotFoundException e) {
863
			logMetacat.error("ReplicationService.handleForceReplicateDeleteRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
864
			logReplication.error("document " + docid
865
					+ " failed to delete because " + e.getMessage());
866
			logReplication.error("ReplicationService.handleForceReplicateDeleteRequest - error: " + e.getMessage());
867
		} catch (InsufficientKarmaException e) {
868
			logMetacat.error("ReplicationService.handleForceReplicateDeleteRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
869
			logReplication.error("document " + docid
870
					+ " failed to delete because " + e.getMessage());
871
			logReplication.error("ReplicationService.handleForceReplicateDeleteRequest - error: " + e.getMessage());
872
		} catch (SQLException e) {
873
			logMetacat.error("ReplicationService.handleForceReplicateDeleteRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
874
			logReplication.error("document " + docid
875
					+ " failed to delete because " + e.getMessage());
876
			logReplication.error("ReplicationService.handleForceReplicateDeleteRequest - error: " + e.getMessage());
877
		} catch (Exception e) {
878
			logMetacat.error("ReplicationService.handleForceReplicateDeleteRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
879
			logReplication.error("document " + docid
880
					+ " failed to delete because " + e.getMessage());
881
			logReplication.error("ReplicationService.handleForceReplicateDeleteRequest - error: " + e.getMessage());
882

    
883
		}//catch
884

    
885
	}
886

    
887
	/**
888
	 * when a forcereplication data file request comes in, local host sends a
889
	 * readdata request to the requesting server (remote server) for the specified
890
	 * docid. Then store it in local database and file system
891
	 */
892
	protected static void handleForceReplicateDataFileRequest(Hashtable<String, String[]> params,
893
			HttpServletRequest request) {
894

    
895
		//make sure there is some parameters
896
		if (params.isEmpty()) {
897
			return;
898
		}
899
		// Get remote server
900
		String server = ((String[]) params.get("server"))[0];
901
		// the docid should include rev number
902
		String docid = ((String[]) params.get("docid"))[0];
903
		// Make sure there is a docid and server
904
		if (docid == null || server == null || server.equals("")) {
905
			logMetacat.error("ReplicationService.handleForceReplicateDataFileRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
906
			logReplication.error("ReplicationService.handleForceReplicateDataFileRequest - Didn't specify docid or server for replication");
907
			return;
908
		}
909

    
910
		// Overide or not
911
		//    boolean override = false;
912
		// dbaction - update or insert
913
		String dbaction = null;
914

    
915
		try {
916
			//docid was switch to two parts uinque code and rev
917
			//String uniqueCode=MetacatUtil.getDocIdFromString(docid);
918
			//int rev=MetacatUtil.getVersionFromString(docid);
919
			if (params.containsKey("dbaction")) {
920
				dbaction = ((String[]) params.get("dbaction"))[0];
921
			} else//default value is update
922
			{
923
//				dbaction = "update";
924
				dbaction = null;
925
			}
926

    
927
			logReplication.info("ReplicationService.handleForceReplicateDataFileRequest - Force replication request from: " + server);
928
			logReplication.info("ReplicationService.handleForceReplicateDataFileRequest - Force replication docid: " + docid);
929
			logReplication.info("ReplicationService.handleForceReplicateDataFileRequest - Force replication action: " + dbaction);
930
			// get the document info from server
931
			URL docinfourl = new URL("https://" + server + "?server="
932
					+ MetacatUtil.getLocalReplicationServerName()
933
					+ "&action=getdocumentinfo&docid=" + docid);
934

    
935
			String docInfoStr = ReplicationService.getURLContent(docinfourl);
936
			
937
			// strip out the system metadata portion
938
		    String systemMetadataXML = ReplicationUtil.getSystemMetadataContent(docInfoStr);
939
		   	docInfoStr = ReplicationUtil.getContentWithoutSystemMetadata(docInfoStr);
940

    
941
			//dih is the parser for the docinfo xml format
942
			DocInfoHandler dih = new DocInfoHandler();
943
			XMLReader docinfoParser = ReplicationHandler.initParser(dih);
944
			docinfoParser.parse(new InputSource(new StringReader(docInfoStr)));
945
			Hashtable<String, String> docinfoHash = dih.getDocInfo();
946
			
947
			String docName = (String) docinfoHash.get("docname");
948

    
949
			String docType = (String) docinfoHash.get("doctype");
950

    
951
			String docHomeServer = (String) docinfoHash.get("home_server");
952
			
953
			String createdDateString = docinfoHash.get("date_created");
954
			String updatedDateString = docinfoHash.get("date_updated");
955
			
956
			Date createdDate = DateTimeMarshaller.deserializeDateToUTC(createdDateString);
957
			Date updatedDate = DateTimeMarshaller.deserializeDateToUTC(updatedDateString);
958
			
959
			logReplication.info("ReplicationService.handleForceReplicateDataFileRequest - docHomeServer of datafile: " + docHomeServer);
960

    
961
			// in case we have a write exception, we still want to track access and sysmeta
962
			Exception writeException = null;
963

    
964
			// do we need the object content?
965
			if (dbaction != null && (dbaction.equals("insert") || dbaction.equals("update"))) {
966
				//Get data file and store it into local file system.
967
				// sending back readdata request to server
968
				URL url = new URL("https://" + server + "?server="
969
						+ MetacatUtil.getLocalReplicationServerName()
970
						+ "&action=readdata&docid=" + docid);
971
				String datafilePath = PropertyService
972
						.getProperty("application.datafilepath");
973

    
974
				
975
				
976
				//register data file into xml_documents table and write data file
977
				//into file system
978
				InputStream inputStream = null;
979
				try {
980
				    inputStream = getURLStream(url);
981
					DocumentImpl.writeDataFileInReplication(inputStream,
982
							datafilePath, docName, docType, docid, null, docHomeServer,
983
							server, DocumentImpl.DOCUMENTTABLE, false, createdDate,
984
							updatedDate);
985
				} catch (Exception e) {
986
					writeException = e;
987
				} finally {
988
				    IOUtils.closeQuietly(inputStream);
989
				}
990

    
991
			}
992
			
993
			// process the real owner and updater
994
			DBConnection dbConn = DBConnectionPool.getDBConnection("ReplicationService.handleForceDataFileRequest");
995
	        int serialNumber = dbConn.getCheckOutSerialNumber();
996
	        dbConn.setAutoCommit(false);
997
			String user = (String) docinfoHash.get("user_owner");
998
			String updated = (String) docinfoHash.get("user_updated");
999
	        updateUserOwner(dbConn, docid, user, updated);
1000
	        DBConnectionPool.returnDBConnection(dbConn, serialNumber);
1001
	        
1002
			// process system metadata
1003
	        if (systemMetadataXML != null) {
1004
	      	  SystemMetadata sysMeta = 
1005
	      		TypeMarshaller.unmarshalTypeFromStream(
1006
	      				  SystemMetadata.class, 
1007
	      				  new ByteArrayInputStream(systemMetadataXML.getBytes("UTF-8")));
1008
	      	  
1009
	      	  // need the guid-to-docid mapping
1010
	      	  boolean mappingExists = true;
1011
	      	  mappingExists = IdentifierManager.getInstance().mappingExists(sysMeta.getIdentifier().getValue());
1012
	      	  if (!mappingExists) {
1013
	      		  IdentifierManager.getInstance().createMapping(sysMeta.getIdentifier().getValue(), docid);
1014
	      	  }
1015
	      	  // save the system metadata
1016
	      	  HazelcastService.getInstance().getSystemMetadataMap().put(sysMeta.getIdentifier(), sysMeta);
1017
	      	  // submit for indexing
1018
              MetacatSolrIndex.getInstance().submit(sysMeta.getIdentifier(), sysMeta, null, true);
1019
	        }
1020
	        
1021
	        // process the access control
1022
	        try {
1023
	        	// check if we had a guid -> docid mapping
1024
	        	String docidNoRev = DocumentUtil.getDocIdFromAccessionNumber(docid);
1025
	        	int rev = DocumentUtil.getRevisionFromAccessionNumber(docid);
1026
	        	IdentifierManager.getInstance().getGUID(docidNoRev, rev);
1027
	        	// no need to create the mapping if we have it
1028
	        } catch (McdbDocNotFoundException mcdbe) {
1029
	        	// create mapping if we don't
1030
	        	IdentifierManager.getInstance().createMapping(docid, docid);
1031
	        }
1032
	        Vector<XMLAccessDAO> accessControlList = dih.getAccessControlList();
1033
	        if (accessControlList != null) {
1034
	        	AccessControlForSingleFile acfsf = new AccessControlForSingleFile(docid);
1035
	        	for (XMLAccessDAO xmlAccessDAO : accessControlList) {
1036
	        		if (!acfsf.accessControlExists(xmlAccessDAO)) {
1037
	        			acfsf.insertPermissions(xmlAccessDAO);
1038
						logReplication.info("ReplicationService.handleForceReplicateRequest - document " + docid
1039
								+ " permissions added to DB");
1040
	        		}
1041
	            }
1042
	        }
1043
	        
1044
	        // throw the write exception now -- this happens when access changes on an object
1045
			if (writeException != null) {
1046
				throw writeException;
1047
			}
1048

    
1049
			logReplication.info("ReplicationService.handleForceReplicateDataFileRequest - datafile " + docid + " added to DB with "
1050
					+ "action " + dbaction);
1051
			EventLog.getInstance().log(request.getRemoteAddr(), request.getHeader("User-Agent"), REPLICATIONUSER,
1052
					docid, dbaction);
1053

    
1054
		} catch (Exception e) {
1055
			e.printStackTrace();
1056
			logMetacat.error("ReplicationService.handleForceReplicateDataFileRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG, e);                         
1057
			logReplication.error("ReplicationService.handleForceReplicateDataFileRequest - Datafile " + docid
1058
					+ " failed to added to DB with " + "action " + dbaction + " because "
1059
					+ e.getMessage());
1060
			logReplication.error("ReplicationService.handleForceReplicateDataFileRequest - ERROR in MetacatReplication.handleForceDataFileReplicate"
1061
					+ "Request(): " + e.getMessage());
1062
		}
1063
	}
1064

    
1065
	/**
1066
	 * Grants or denies a lock to a requesting host.
1067
	 * The servlet parameters of interrest are:
1068
	 * docid: the docid of the file the lock is being requested for
1069
	 * currentdate: the timestamp of the document on the remote server
1070
	 *
1071
	 */
1072
	protected static void handleGetLockRequest(
1073
			Hashtable<String, String[]> params, HttpServletResponse response) {
1074

    
1075
		try {
1076

    
1077
			String docid = ((String[]) params.get("docid"))[0];
1078
			String remoteRev = ((String[]) params.get("updaterev"))[0];
1079
			DocumentImpl requestDoc = new DocumentImpl(docid);
1080
			logReplication.info("ReplicationService.handleGetLockRequest - lock request for " + docid);
1081
			int localRevInt = requestDoc.getRev();
1082
			int remoteRevInt = Integer.parseInt(remoteRev);
1083

    
1084
			// get a writer for sending back to response
1085
			response.setContentType("text/xml");
1086
			Writer out = response.getWriter();
1087
			
1088
			if (remoteRevInt >= localRevInt) {
1089
				if (!fileLocks.contains(docid)) { //grant the lock if it is not already locked
1090
					fileLocks.add(0, docid); //insert at the beginning of the queue Vector
1091
					//send a message back to the the remote host authorizing the insert
1092
					out.write("<lockgranted><docid>" + docid
1093
									+ "</docid></lockgranted>");
1094
					//          lockThread = new Thread(this);
1095
					//          lockThread.setPriority(Thread.MIN_PRIORITY);
1096
					//          lockThread.start();
1097
					logReplication.info("ReplicationService.handleGetLockRequest - lock granted for " + docid);
1098
				} else { //deny the lock
1099
					out.write("<filelocked><docid>" + docid + "</docid></filelocked>");
1100
					logReplication.info("ReplicationService.handleGetLockRequest - lock denied for " + docid
1101
							+ "reason: file already locked");
1102
				}
1103
			} else {//deny the lock.
1104
				out.write("<outdatedfile><docid>" + docid + "</docid></filelocked>");
1105
				logReplication.info("ReplicationService.handleGetLockRequest - lock denied for " + docid
1106
						+ "reason: client has outdated file");
1107
			}
1108
			out.close();
1109
			//conn.close();
1110
		} catch (Exception e) {
1111
			logMetacat.error("ReplicationService.handleGetLockRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
1112
			logReplication.error("ReplicationService.handleGetLockRequest - error requesting file lock from MetacatReplication."
1113
					+ "handleGetLockRequest: " + e.getMessage());
1114
			e.printStackTrace(System.out);
1115
		}
1116
	}
1117

    
1118
	/**
1119
	 * Sends all of the xml_documents information encoded in xml to a requestor
1120
	 * the format is:
1121
	 * <!ELEMENT documentinfo (docid, docname, doctype, doctitle, user_owner,
1122
	 *                  user_updated, home_server, public_access, rev)/>
1123
	 * all of the subelements of document info are #PCDATA
1124
	 */
1125
	protected static void handleGetDocumentInfoRequest(
1126
			Hashtable<String, String[]> params, HttpServletResponse response) {
1127
		String docid = ((String[]) (params.get("docid")))[0];
1128

    
1129
		try {
1130
			// get docinfo as XML string
1131
			String docinfoXML = getDocumentInfo(docid);
1132
			
1133
			// get a writer for sending back to response
1134
			response.setContentType("text/xml");
1135
			Writer out = response.getWriter();
1136
			out.write(docinfoXML);
1137
			out.close();
1138

    
1139
		} catch (Exception e) {
1140
			logMetacat.error("ReplicationService.handleGetDocumentInfoRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
1141
			logReplication.error("ReplicationService.handleGetDocumentInfoRequest - error in metacatReplication.handlegetdocumentinforequest "
1142
					+ "for doc: " + docid + " : " + e.getMessage());
1143
		}
1144

    
1145
	}
1146
	
1147
	public static Hashtable<String, String> getDocumentInfoMap(String docid)
1148
			throws HandlerException, AccessControlException, JiBXException,
1149
			IOException, McdbException, SAXException {
1150
		
1151
		// Try get docid info from remote server
1152
		DocInfoHandler dih = new DocInfoHandler();
1153
		XMLReader docinfoParser = ReplicationHandler.initParser(dih);
1154

    
1155
		String docInfoStr = getDocumentInfo(docid);
1156

    
1157
		// strip out the system metadata portion
1158
		String systemMetadataXML = ReplicationUtil.getSystemMetadataContent(docInfoStr);
1159
		docInfoStr = ReplicationUtil.getContentWithoutSystemMetadata(docInfoStr);
1160

    
1161
		docinfoParser.parse(new InputSource(new StringReader(docInfoStr)));
1162
		Hashtable<String, String> docinfoHash = dih.getDocInfo();
1163

    
1164
		return docinfoHash;
1165
	}
1166
	
1167
	/**
1168
	 * Gets a docInfo XML snippet for the replication API
1169
	 * @param docid
1170
	 * @return
1171
	 * @throws AccessControlException
1172
	 * @throws JiBXException
1173
	 * @throws IOException
1174
	 * @throws McdbException
1175
	 */
1176
	public static String getDocumentInfo(String docid) throws AccessControlException, JiBXException, IOException, McdbException {
1177
		StringBuffer sb = new StringBuffer();
1178

    
1179
		DocumentImpl doc = new DocumentImpl(docid);
1180
		sb.append("<documentinfo><docid>").append(docid);
1181
		sb.append("</docid>");
1182
		
1183
		try {
1184
			// serialize the System Metadata as XML for docinfo
1185
			String guid = IdentifierManager.getInstance().getGUID(doc.getDocID(), doc.getRev());
1186
			SystemMetadata systemMetadata = IdentifierManager.getInstance().getSystemMetadata(guid);
1187
			ByteArrayOutputStream baos = new ByteArrayOutputStream();
1188
			TypeMarshaller.marshalTypeToOutputStream(systemMetadata, baos);
1189
			String systemMetadataXML = baos.toString("UTF-8");
1190
			sb.append("<systemMetadata>");
1191
			sb.append(systemMetadataXML);
1192
			sb.append("</systemMetadata>");
1193
		} catch(McdbDocNotFoundException e) {
1194
		  logMetacat.warn("No SystemMetadata found for: " + docid);
1195
		}
1196
		
1197
		Calendar created = Calendar.getInstance();
1198
		created.setTime(doc.getCreateDate());
1199
		Calendar updated = Calendar.getInstance();
1200
		updated.setTime(doc.getUpdateDate());
1201
		
1202
		sb.append("<docname><![CDATA[").append(doc.getDocname());
1203
		sb.append("]]></docname><doctype>").append(doc.getDoctype());
1204
		sb.append("</doctype>");
1205
		sb.append("<user_owner>").append(doc.getUserowner());
1206
		sb.append("</user_owner><user_updated>").append(doc.getUserupdated());
1207
		sb.append("</user_updated>");
1208
		sb.append("<date_created>");
1209
		sb.append(DateTimeMarshaller.serializeDateToUTC(doc.getCreateDate()));
1210
		sb.append("</date_created>");
1211
		sb.append("<date_updated>");
1212
		sb.append(DateTimeMarshaller.serializeDateToUTC(doc.getUpdateDate()));
1213
		sb.append("</date_updated>");
1214
		sb.append("<home_server>");
1215
		sb.append(doc.getDocHomeServer());
1216
		sb.append("</home_server>");
1217
		sb.append("<public_access>").append(doc.getPublicaccess());
1218
		sb.append("</public_access><rev>").append(doc.getRev());
1219
		sb.append("</rev>");
1220

    
1221
		sb.append("<accessControl>");
1222

    
1223
		AccessControlForSingleFile acfsf = new AccessControlForSingleFile(docid); 
1224
		sb.append(acfsf.getAccessString());
1225
		
1226
		sb.append("</accessControl>");
1227

    
1228
		sb.append("</documentinfo>");
1229
			
1230
		return sb.toString();
1231
	}
1232
	
1233
	/**
1234
	 * Sends System Metadata as XML
1235
	 */
1236
	protected static void handleGetSystemMetadataRequest(
1237
			Hashtable<String, String[]> params, HttpServletResponse response) {
1238
		String guid = ((String[]) (params.get("guid")))[0];
1239
		String systemMetadataXML = null;
1240
		try {
1241
			
1242
			// serialize the System Metadata as XML 
1243
			SystemMetadata systemMetadata = IdentifierManager.getInstance().getSystemMetadata(guid);
1244
			ByteArrayOutputStream baos = new ByteArrayOutputStream();
1245
			TypeMarshaller.marshalTypeToOutputStream(systemMetadata, baos);
1246
			systemMetadataXML = baos.toString("UTF-8");
1247
				
1248
			// get a writer for sending back to response
1249
			response.setContentType("text/xml");
1250
			Writer out = response.getWriter();
1251
			out.write(systemMetadataXML);
1252
			out.close();
1253

    
1254
		} catch (Exception e) {
1255
			String msg = "ReplicationService.handleGetSystemMetadataRequest for guid: " + guid + " : " + e.getMessage();
1256
			logMetacat.error(msg);                         
1257
			logReplication.error(msg);
1258
		}
1259

    
1260
	}
1261
	
1262
	/**
1263
	 * when a forcereplication request comes in, local host sends a read request
1264
	 * to the requesting server (remote server) for the specified docid. Then
1265
	 * store it in local database.
1266
	 */
1267
	protected static void handleForceReplicateSystemMetadataRequest(
1268
			Hashtable<String, String[]> params, HttpServletResponse response,
1269
			HttpServletRequest request) {
1270
		String server = ((String[]) params.get("server"))[0]; // the server that
1271
		String guid = ((String[]) params.get("guid"))[0]; // sent the document
1272
		
1273
		try {
1274
			logReplication.info("ReplicationService.handleForceReplicateSystemMetadataRequest - Force replication system metadata request from: " + server);
1275
			// get the system metadata from server
1276
			URL docinfourl = new URL("https://" + server + "?server="
1277
					+ MetacatUtil.getLocalReplicationServerName()
1278
					+ "&action=getsystemmetadata&guid=" + guid);
1279
			
1280
			String systemMetadataXML = ReplicationService.getURLContent(docinfourl);
1281
						
1282
			// process system metadata
1283
			if (systemMetadataXML != null) {
1284
				SystemMetadata sysMeta = 
1285
					TypeMarshaller.unmarshalTypeFromStream(
1286
							SystemMetadata.class,
1287
							new ByteArrayInputStream(systemMetadataXML.getBytes("UTF-8")));
1288
				HazelcastService.getInstance().getSystemMetadataMap().put(sysMeta.getIdentifier(), sysMeta);
1289
				// submit for indexing
1290
                MetacatSolrIndex.getInstance().submit(sysMeta.getIdentifier(), sysMeta, null, true);
1291
			}
1292
      
1293
			logReplication.info("ReplicationService.handleForceReplicateSystemMetadataRequest - processed guid: " + guid);
1294
			EventLog.getInstance().log(request.getRemoteAddr(), request.getHeader("User-Agent"), REPLICATIONUSER, guid, "systemMetadata");
1295

    
1296
		} catch (Exception e) {
1297
			logMetacat.error("ReplicationService.handleForceReplicateRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG, e);                         
1298
			logReplication.error("ReplicationService.handleForceReplicateRequest - General error when processing guid: " + guid, e);
1299
		}
1300
	}
1301

    
1302
	/**
1303
	 * Sends a datafile to a remote host
1304
	 */
1305
	protected static void handleGetDataFileRequest(OutputStream outPut,
1306
			Hashtable<String, String[]> params, HttpServletResponse response)
1307

    
1308
	{
1309
		// File path for data file
1310
		String filepath;
1311
		// Request docid
1312
		String docId = ((String[]) (params.get("docid")))[0];
1313
		//check if the doicd is null
1314
		if (docId == null) {
1315
			logMetacat.error("ReplicationService.handleGetDataFileRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
1316
			logReplication.error("ReplicationService.handleGetDataFileRequest - Didn't specify docid for replication");
1317
			return;
1318
		}
1319

    
1320
		//try to open a https stream to test if the request server's public key
1321
		//in the key store, this is security issue
1322
		try {
1323
			filepath = PropertyService.getProperty("application.datafilepath");
1324
			String server = params.get("server")[0];
1325
			URL u = new URL("https://" + server + "?server="
1326
					+ MetacatUtil.getLocalReplicationServerName() + "&action=test");
1327
			String test = ReplicationService.getURLContent(u);
1328
			//couldn't pass the test
1329
			if (test.indexOf("successfully") == -1) {
1330
				//response.setContentType("text/xml");
1331
				//outPut.println("<error>Couldn't pass the trust test</error>");
1332
				logMetacat.error("ReplicationService.handleGetDataFileRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
1333
				logReplication.error("ReplicationService.handleGetDataFileRequest - Couldn't pass the trust test");
1334
				return;
1335
			}
1336
		}//try
1337
		catch (Exception ee) {
1338
			return;
1339
		}//catch
1340

    
1341
		if (!filepath.endsWith("/")) {
1342
			filepath += "/";
1343
		}
1344
		// Get file aboslute file name
1345
		String filename = filepath + docId;
1346

    
1347
		//MIME type
1348
		String contentType = null;
1349
		if (filename.endsWith(".xml")) {
1350
			contentType = "text/xml";
1351
		} else if (filename.endsWith(".css")) {
1352
			contentType = "text/css";
1353
		} else if (filename.endsWith(".dtd")) {
1354
			contentType = "text/plain";
1355
		} else if (filename.endsWith(".xsd")) {
1356
			contentType = "text/xml";
1357
		} else if (filename.endsWith("/")) {
1358
			contentType = "text/html";
1359
		} else {
1360
			File f = new File(filename);
1361
			if (f.isDirectory()) {
1362
				contentType = "text/html";
1363
			} else {
1364
				contentType = "application/octet-stream";
1365
			}
1366
		}
1367

    
1368
		// Set the mime type
1369
		response.setContentType(contentType);
1370

    
1371
		// Get the content of the file
1372
		FileInputStream fin = null;
1373
		try {
1374
			// FileInputStream to metacat
1375
			fin = new FileInputStream(filename);
1376
			// 4K buffer
1377
			byte[] buf = new byte[4 * 1024];
1378
			// Read data from file input stream to byte array
1379
			int b = fin.read(buf);
1380
			// Write to outStream from byte array
1381
			while (b != -1) {
1382
				outPut.write(buf, 0, b);
1383
				b = fin.read(buf);
1384
			}
1385
			// close file input stream
1386
			fin.close();
1387

    
1388
		} catch (Exception e) {
1389
			logMetacat.error("ReplicationService.handleGetDataFileRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
1390
			logReplication.error("ReplicationService.handleGetDataFileRequest - error getting data file from MetacatReplication."
1391
					+ "handlGetDataFileRequest " + e.getMessage());
1392
			e.printStackTrace(System.out);
1393
		} finally {
1394
		    IOUtils.closeQuietly(fin);
1395
		}
1396

    
1397
	}
1398

    
1399
	/**
1400
	 * Sends a document to a remote host
1401
	 */
1402
	protected static void handleGetDocumentRequest(
1403
			Hashtable<String, String[]> params, HttpServletResponse response) {
1404

    
1405
		String urlString = null;
1406
		String documentPath = null;
1407
		String errorMsg = null;
1408
		FileOutputStream fos = null;
1409
		InputStream is = null;
1410
		OutputStream outputStream = null;
1411
		try {
1412
			// try to open a https stream to test if the request server's public
1413
			// key
1414
			// in the key store, this is security issue
1415
			String server = params.get("server")[0];
1416
			urlString = "https://" + server + "?server="
1417
					+ MetacatUtil.getLocalReplicationServerName() + "&action=test";
1418
			URL u = new URL(urlString);
1419
			String test = ReplicationService.getURLContent(u);
1420
			// couldn't pass the test
1421
			if (test.indexOf("successfully") == -1) {
1422
				response.setContentType("text/xml");
1423
				Writer out = response.getWriter();
1424
				out.write("<error>Couldn't pass the trust test " + test + " </error>");
1425
				out.close();
1426
				return;
1427
			}
1428

    
1429
			String docid = params.get("docid")[0];
1430
			logReplication.debug("ReplicationService.handleGetDocumentRequest - MetacatReplication.handleGetDocumentRequest for docid: "
1431
					+ docid);
1432
			DocumentImpl di = new DocumentImpl(docid);
1433

    
1434
			String documentDir = PropertyService
1435
					.getProperty("application.documentfilepath");
1436
			documentPath = documentDir + FileUtil.getFS() + docid;
1437

    
1438
			// if the document does not exist on disk, read it from db and write
1439
			// it to disk.
1440
			if (FileUtil.getFileStatus(documentPath) == FileUtil.DOES_NOT_EXIST
1441
					|| FileUtil.getFileSize(documentPath) == 0) {
1442
				fos = new FileOutputStream(documentPath);
1443
				is = di.toXml(fos, null, null, true);
1444
				fos.close();
1445
				is.close();
1446
			}
1447

    
1448
			// read the file from disk and send it to outputstream
1449
			outputStream = response.getOutputStream();
1450
			is = di.readFromFileSystem(outputStream, null, null, documentPath);
1451
			is.close();
1452
			outputStream.close();
1453

    
1454
			logReplication.info("ReplicationService.handleGetDocumentRequest - document " + docid + " sent");
1455

    
1456
			// return to avoid continuing to the error reporting section at the end
1457
			return;
1458
			
1459
		} catch (MalformedURLException mue) {
1460
			logMetacat.error("ReplicationService.handleGetDocumentRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
1461
			logReplication.error("ReplicationService.handleGetDocumentRequest - Url error when getting document from MetacatReplication."
1462
					+ "handlGetDocumentRequest for url: " + urlString + " : "
1463
					+ mue.getMessage());
1464
			// e.printStackTrace(System.out);
1465
			
1466
		} catch (IOException ioe) {
1467
			logMetacat.error("ReplicationService.handleGetDocumentRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
1468
			logReplication.error("ReplicationService.handleGetDocumentRequest - I/O error when getting document from MetacatReplication."
1469
					+ "handlGetDocumentRequest for file: " + documentPath + " : "
1470
					+ ioe.getMessage());
1471
			errorMsg = ioe.getMessage();
1472
		} catch (PropertyNotFoundException pnfe) {
1473
			logMetacat.error("ReplicationService.handleGetDocumentRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
1474
			logReplication
1475
					.error("ReplicationService.handleGetDocumentRequest - Error getting property when getting document from MetacatReplication."
1476
							+ "handlGetDocumentRequest for file: "
1477
							+ documentPath
1478
							+ " : "
1479
							+ pnfe.getMessage());
1480
			// e.printStackTrace(System.out);
1481
			errorMsg = pnfe.getMessage();
1482
		} catch (McdbException me) {
1483
			logReplication
1484
					.error("ReplicationService.handleGetDocumentRequest - Document implementation error  getting property when getting document from MetacatReplication."
1485
							+ "handlGetDocumentRequest for file: "
1486
							+ documentPath
1487
							+ " : "
1488
							+ me.getMessage());
1489
			// e.printStackTrace(System.out);
1490
			errorMsg = me.getMessage();
1491
		} catch (Exception e) {
1492
			logReplication
1493
			.error("ReplicationService.handleGetDocumentRequest - General exception encountered."
1494
					+ "handlGetDocumentRequest for file: "
1495
					+ documentPath
1496
					+ " : "
1497
					+ e.getMessage());
1498
			// e.printStackTrace(System.out);
1499
			errorMsg = e.getMessage();
1500
		} finally {
1501
            IOUtils.closeQuietly(fos);
1502
            IOUtils.closeQuietly(is);
1503
            IOUtils.closeQuietly(outputStream);
1504
		}
1505
		
1506
		// report any errors if we got here
1507
		response.setContentType("text/xml");
1508
		Writer out = null;
1509
		try {
1510
			response.getWriter();
1511
			out = response.getWriter();
1512
			out.write("<error>" + errorMsg + "</error>");
1513
		} catch (Exception e) {
1514
			logMetacat.error(e.getMessage(), e);
1515
		} finally {
1516
			try {
1517
				out.close();
1518
			} catch (IOException e) {
1519
				logMetacat.error(e.getMessage(), e);
1520
			}
1521
		}
1522
		
1523

    
1524
	}
1525

    
1526
	/**
1527
	 * Sends a list of all of the documents on this sever along with their
1528
	 * revision numbers. The format is: <!ELEMENT replication (server, updates)>
1529
	 * <!ELEMENT server (#PCDATA)> <!ELEMENT updates ((updatedDocument |
1530
	 * deleteDocument | revisionDocument)*)> <!ELEMENT updatedDocument (docid,
1531
	 * rev, datafile*)> <!ELEMENT deletedDocument (docid, rev)> <!ELEMENT
1532
	 * revisionDocument (docid, rev, datafile*)> <!ELEMENT docid (#PCDATA)>
1533
	 * <!ELEMENT rev (#PCDATA)> <!ELEMENT datafile (#PCDATA)> note that the rev
1534
	 * in deletedDocument is always empty. I just left it in there to make the
1535
	 * parser implementation easier.
1536
	 */
1537
	protected static void handleUpdateRequest(Hashtable<String, String[]> params,
1538
			HttpServletResponse response) {
1539
		// Checked out DBConnection
1540
		DBConnection dbConn = null;
1541
		// DBConenction serial number when checked it out
1542
		int serialNumber = -1;
1543
		PreparedStatement pstmt = null;
1544
		// Server list to store server info of xml_replication table
1545
		ReplicationServerList serverList = null;
1546
		
1547
		// a writer for response
1548
		Writer out = null;
1549

    
1550
		try {
1551
			// get writer, TODO: encoding?
1552
			response.setContentType("text/xml");
1553
			out = response.getWriter();
1554
			
1555
			// Check out a DBConnection from pool
1556
			dbConn = DBConnectionPool
1557
					.getDBConnection("MetacatReplication.handleUpdateRequest");
1558
			serialNumber = dbConn.getCheckOutSerialNumber();
1559
			// Create a server list from xml_replication table
1560
			serverList = new ReplicationServerList();
1561

    
1562
			// Get remote server name from param
1563
			String server = ((String[]) params.get("server"))[0];
1564
			// If no servr name in param, return a error
1565
			if (server == null || server.equals("")) {
1566
				out.write("<error>Request didn't specify server name</error>");
1567
				out.close();
1568
				return;
1569
			}//if
1570

    
1571
			//try to open a https stream to test if the request server's public key
1572
			//in the key store, this is security issue
1573
			String testUrl = "https://" + server + "?server="
1574
            + MetacatUtil.getLocalReplicationServerName() + "&action=test";
1575
			logReplication.info("Running trust test: " + testUrl);
1576
			URL u = new URL(testUrl);
1577
			String test = ReplicationService.getURLContent(u);
1578
			logReplication.info("Ouput from test is '" + test + "'");
1579
			//couldn't pass the test
1580
			if (test.indexOf("successfully") == -1) {
1581
			    logReplication.error("Trust test failed.");
1582
				out.write("<error>Couldn't pass the trust test</error>");
1583
				out.close();
1584
				return;
1585
			}
1586
			logReplication.info("Trust test succeeded.");
1587

    
1588
			// Check if local host configure to replicate xml documents to remote
1589
			// server. If not send back a error message
1590
			if (!serverList.getReplicationValue(server)) {
1591
				out.write("<error>Configuration not allow to replicate document to you</error>");
1592
				out.close();
1593
				return;
1594
			}//if
1595

    
1596
			// Store the sql command
1597
			StringBuffer docsql = new StringBuffer();
1598
			StringBuffer revisionSql = new StringBuffer();
1599
			
1600
			// Store the data set file
1601
			Vector<Vector<String>> packageFiles = new Vector<Vector<String>>();
1602

    
1603
			// Append local server's name and replication servlet to doclist
1604
			out.append("<?xml version=\"1.0\"?><replication>");
1605
			out.append("<server>")
1606
					.append(MetacatUtil.getLocalReplicationServerName());
1607
			//doclist.append(util.getProperty("replicationpath"));
1608
			out.append("</server><updates>");
1609

    
1610
			// Get correct docid that reside on this server according the requesting
1611
			// server's replicate and data replicate value in xml_replication table
1612
			docsql.append(DatabaseService.getInstance().getDBAdapter().getReplicationDocumentListSQL());
1613
			//docsql.append("select docid, rev, doctype from xml_documents where (docid not in (select a.docid from xml_documents a, xml_revisions b where a.docid=b.docid and a.rev<=b.rev)) ");
1614
			revisionSql.append("select docid, rev, doctype from xml_revisions ");
1615
			// If the localhost is not a hub to the remote server, only replicate
1616
			// the docid' which home server is local host (server_location =1)
1617
			if (!serverList.getHubValue(server)) {
1618
				String serverLocationDoc = " and a.server_location = 1";
1619
				String serverLocationRev = "where server_location = 1";
1620
				docsql.append(serverLocationDoc);
1621
				revisionSql.append(serverLocationRev);
1622
			}
1623
			logReplication.info("ReplicationService.handleUpdateRequest - Doc sql: " + docsql.toString());
1624

    
1625
			// Get any deleted documents
1626
			StringBuffer delsql = new StringBuffer();
1627
			delsql.append("SELECT t1.docid FROM xml_revisions t1 LEFT JOIN xml_documents t2 on t1.docid = t2.docid WHERE t2.docid IS NULL "); 
1628
			
1629
			// If the localhost is not a hub to the remote server, only replicate
1630
			// the docid' which home server is local host (server_location =1)
1631
			if (!serverList.getHubValue(server)) {
1632
				delsql.append("and t1.server_location = 1");
1633
			}
1634
			logReplication.info("ReplicationService.handleUpdateRequest - Deleted sql: " + delsql.toString());
1635

    
1636
			// Get docid list of local host
1637
			pstmt = dbConn.prepareStatement(docsql.toString());
1638
			pstmt.execute();
1639
			ResultSet rs = pstmt.getResultSet();
1640
			boolean tablehasrows = rs.next();
1641
			//If metacat configed to replicate data file
1642
			//if ((util.getProperty("replicationsenddata")).equals("on"))
1643
			boolean replicateData = serverList.getDataReplicationValue(server);
1644
			if (replicateData) {
1645
				while (tablehasrows) {
1646
					String recordDoctype = rs.getString(3);
1647
					Vector<String> packagedoctypes = MetacatUtil
1648
							.getOptionList(PropertyService
1649
									.getProperty("xml.packagedoctype"));
1650
					//if this is a package file, put it at the end
1651
					//because if a package file is read before all of the files it
1652
					//refers to are loaded then there is an error
1653
					if (recordDoctype != null && !packagedoctypes.contains(recordDoctype)) {
1654
						//If this is not data file
1655
						if (!recordDoctype.equals("BIN")) {
1656
							//for non-data file document
1657
							out.append("<updatedDocument>");
1658
							out.append("<docid>").append(rs.getString(1));
1659
							out.append("</docid><rev>" + rs.getInt(2));
1660
							out.append("</rev>");
1661
							out.append("</updatedDocument>");
1662
						}//if
1663
						else {
1664
							//for data file document, in datafile attributes
1665
							//we put "datafile" value there
1666
							out.append("<updatedDocument>");
1667
							out.append("<docid>").append(rs.getString(1));
1668
							out.append("</docid><rev>" + rs.getInt(2));
1669
							out.append("</rev>");
1670
							out.append("<datafile>");
1671
							out.append(DATA_FILE_FLAG);
1672
							out.append("</datafile>");
1673
							out.append("</updatedDocument>");
1674
						}//else
1675
					}//if packagedoctpes
1676
					else { //the package files are saved to be put into the xml later.
1677
						Vector<String> v = new Vector<String>();
1678
						v.add(rs.getString(1));
1679
						v.add(String.valueOf(rs.getInt(2)));
1680
						packageFiles.add(v);
1681
					}//esle
1682
					tablehasrows = rs.next();
1683
				}//while
1684
			}//if
1685
			else //metacat was configured not to send data file
1686
			{
1687
				while (tablehasrows) {
1688
					String recordDoctype = rs.getString(3);
1689
					if (!recordDoctype.equals("BIN")) { //don't replicate data files
1690
						Vector<String> packagedoctypes = MetacatUtil
1691
								.getOptionList(PropertyService
1692
										.getProperty("xml.packagedoctype"));
1693
						if (recordDoctype != null
1694
								&& !packagedoctypes.contains(recordDoctype)) { //if this is a package file, put it at the end
1695
							//because if a package file is read before all of the files it
1696
							//refers to are loaded then there is an error
1697
							out.append("<updatedDocument>");
1698
							out.append("<docid>" + rs.getString(1));
1699
							out.append("</docid><rev>" + rs.getInt(2));
1700
							out.append("</rev>");
1701
							out.append("</updatedDocument>");
1702
						} else { //the package files are saved to be put into the xml later.
1703
							Vector<String> v = new Vector<String>();
1704
							v.add(rs.getString(1));
1705
							v.add(String.valueOf(rs.getInt(2)));
1706
							packageFiles.add(v);
1707
						}
1708
					}//if
1709
					tablehasrows = rs.next();
1710
				}//while
1711
			}//else
1712

    
1713
			pstmt = dbConn.prepareStatement(delsql.toString());
1714
			//usage count should increas 1
1715
			dbConn.increaseUsageCount(1);
1716

    
1717
			pstmt.execute();
1718
			rs = pstmt.getResultSet();
1719
			tablehasrows = rs.next();
1720
			while (tablehasrows) { //handle the deleted documents
1721
				out.append("<deletedDocument><docid>").append(rs.getString(1));
1722
				out.append("</docid><rev></rev></deletedDocument>");
1723
				//note that rev is always empty for deleted docs
1724
				tablehasrows = rs.next();
1725
			}
1726

    
1727
			//now we can put the package files into the xml results
1728
			for (int i = 0; i < packageFiles.size(); i++) {
1729
				Vector<String> v = packageFiles.elementAt(i);
1730
				out.append("<updatedDocument>");
1731
				out.append("<docid>").append(v.elementAt(0));
1732
				out.append("</docid><rev>");
1733
				out.append(v.elementAt(1));
1734
				out.append("</rev>");
1735
				out.append("</updatedDocument>");
1736
			}
1737
			// add revision doc list  
1738
			out.append(prepareRevisionDoc(dbConn, revisionSql.toString(),
1739
					replicateData));
1740

    
1741
			out.append("</updates></replication>");
1742
			logReplication.info("ReplicationService.handleUpdateRequest - done writing to output stream.");
1743
			pstmt.close();
1744
			//conn.close();
1745

    
1746
		} catch (Exception e) {
1747
			logMetacat.error("ReplicationService.handleUpdateRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
1748
			logReplication.error("ReplicationService.handleUpdateRequest - error in MetacatReplication." + "handleupdaterequest: "
1749
					+ e.getMessage());
1750
			//e.printStackTrace(System.out);
1751
			try {
1752
				out.write("<error>" + e.getMessage() + "</error>");
1753
			} catch (IOException e1) {
1754
				logMetacat.error(e1.getMessage(), e1);
1755
			}
1756
		} finally {
1757
			try {
1758
				pstmt.close();
1759
			}//try
1760
			catch (SQLException ee) {
1761
				logMetacat.error("ReplicationService.handleUpdateRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
1762
				logReplication.error("ReplicationService.handleUpdateRequest - Error in MetacatReplication."
1763
						+ "handleUpdaterequest to close pstmt: " + ee.getMessage());
1764
			}//catch
1765
			finally {
1766
				DBConnectionPool.returnDBConnection(dbConn, serialNumber);
1767
			}//finally
1768
			try {
1769
				out.close();
1770
			} catch (IOException e) {
1771
				logMetacat.error(e.getMessage(), e);
1772
			}
1773
		}//finally
1774

    
1775
	}//handlUpdateRequest
1776

    
1777
	/**
1778
	 * 
1779
	 * @param dbConn connection for doing the update
1780
	 * @param docid the document id to update
1781
	 * @param owner the user_owner
1782
	 * @param updater the user_updated
1783
	 * @throws SQLException
1784
	 */
1785
	public static void updateUserOwner(DBConnection dbConn, String docid, String owner, String updater) throws SQLException {
1786
	
1787
		String sql = 
1788
			"UPDATE xml_documents " +
1789
			"SET user_owner = ?, " +
1790
			"user_updated = ? " +
1791
			"WHERE docid = ?;";
1792
		PreparedStatement pstmt = dbConn.prepareStatement(sql);
1793
		//usage count should increas 1
1794
		dbConn.increaseUsageCount(1);
1795

    
1796
		docid = DocumentUtil.getSmartDocId(docid);
1797
		pstmt.setString(1, owner);
1798
		pstmt.setString(2, updater);
1799
		pstmt.setString(3, docid);
1800
		pstmt.execute();
1801
		pstmt.close();
1802
		
1803
		dbConn.commit();
1804
	}
1805
	
1806
	/*
1807
	 * This method will get the xml string for document in xml_revision
1808
	 * The schema look like <!ELEMENT revisionDocument (docid, rev, datafile*)>
1809
	 */
1810
	private static String prepareRevisionDoc(DBConnection dbConn, String revSql,
1811
			boolean replicateData) throws Exception {
1812
		logReplication.warn("ReplicationService.prepareRevisionDoc - The revision document sql is " + revSql);
1813
		StringBuffer revDocList = new StringBuffer();
1814
		PreparedStatement pstmt = dbConn.prepareStatement(revSql);
1815
		//usage count should increas 1
1816
		dbConn.increaseUsageCount(1);
1817

    
1818
		pstmt.execute();
1819
		ResultSet rs = pstmt.getResultSet();
1820
		logReplication.warn("Processing replication revision for documents");
1821
		while (rs.next()) {
1822
			String recordDoctype = rs.getString(3);
1823

    
1824
			//If this is data file and it isn't configured to replicate data
1825
			if (recordDoctype.equals("BIN") && !replicateData) {
1826
				logMetacat.debug("SKipping data file because data replication is not configured");
1827

    
1828
				// do nothing
1829
			} else {
1830
				String docid = rs.getString(1);
1831
				int rev = rs.getInt(2);
1832
				logMetacat.debug("Processing replication revision for docid: " + docid + "." + rev);
1833

    
1834
				revDocList.append("<revisionDocument>");
1835
				revDocList.append("<docid>").append(docid);
1836
				revDocList.append("</docid><rev>").append(rev);
1837
				revDocList.append("</rev>");
1838
				// data file
1839
				if (recordDoctype.equals("BIN")) {
1840
					revDocList.append("<datafile>");
1841
					revDocList.append(DATA_FILE_FLAG);
1842
					revDocList.append("</datafile>");
1843
				}
1844
				revDocList.append("</revisionDocument>");
1845

    
1846
			}//else
1847
		}
1848
		//System.out.println("The revision list is"+ revDocList.toString());
1849
		return revDocList.toString();
1850
	}
1851

    
1852
	/**
1853
	 * Returns the xml_catalog table encoded in xml
1854
	 */
1855
	public static String getCatalogXML() {
1856
		return handleGetCatalogRequest(null, null, false);
1857
	}
1858

    
1859
	/**
1860
	 * Sends the contents of the xml_catalog table encoded in xml
1861
	 * The xml format is:
1862
	 * <!ELEMENT xml_catalog (row*)>
1863
	 * <!ELEMENT row (entry_type, source_doctype, target_doctype, public_id,
1864
	 *                system_id)>
1865
	 * All of the sub elements of row are #PCDATA
1866

    
1867
	 * If printFlag == false then do not print to out.
1868
	 */
1869
	protected static String handleGetCatalogRequest(
1870
			Hashtable<String, String[]> params, HttpServletResponse response,
1871
			boolean printFlag) {
1872
		DBConnection dbConn = null;
1873
		int serialNumber = -1;
1874
		PreparedStatement pstmt = null;
1875
		Writer out = null;
1876
		try {
1877
			// get writer, TODO: encoding?
1878
		    if(printFlag)
1879
		    {
1880
		        response.setContentType("text/xml");
1881
		        out = response.getWriter();
1882
		    }
1883
			/*conn = MetacatReplication.getDBConnection("MetacatReplication." +
1884
			                                          "handleGetCatalogRequest");*/
1885
			dbConn = DBConnectionPool
1886
					.getDBConnection("MetacatReplication.handleGetCatalogRequest");
1887
			serialNumber = dbConn.getCheckOutSerialNumber();
1888
			pstmt = dbConn.prepareStatement("select entry_type, "
1889
					+ "source_doctype, target_doctype, public_id, "
1890
					+ "system_id from xml_catalog");
1891
			pstmt.execute();
1892
			ResultSet rs = pstmt.getResultSet();
1893
			boolean tablehasrows = rs.next();
1894
			StringBuffer sb = new StringBuffer();
1895
			sb.append("<?xml version=\"1.0\"?><xml_catalog>");
1896
			while (tablehasrows) {
1897
				sb.append("<row><entry_type>").append(rs.getString(1));
1898
				sb.append("</entry_type><source_doctype>").append(rs.getString(2));
1899
				sb.append("</source_doctype><target_doctype>").append(rs.getString(3));
1900
				sb.append("</target_doctype><public_id>").append(rs.getString(4));
1901
				// system id may not have server url on front.  Add it if not.
1902
				String systemID = rs.getString(5);
1903
				if (!systemID.startsWith("http://")) {
1904
					systemID = SystemUtil.getContextURL() + systemID;
1905
				}
1906
				sb.append("</public_id><system_id>").append(systemID);
1907
				sb.append("</system_id></row>");
1908

    
1909
				tablehasrows = rs.next();
1910
			}
1911
			sb.append("</xml_catalog>");
1912
			//conn.close();
1913
			if (printFlag) {
1914
				response.setContentType("text/xml");
1915
				out.write(sb.toString());
1916
			}
1917
			pstmt.close();
1918
			return sb.toString();
1919
		} catch (Exception e) {
1920
			logMetacat.error("ReplicationService.handleGetCatalogRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
1921
			logReplication.error("ReplicationService.handleGetCatalogRequest - error in MetacatReplication.handleGetCatalogRequest:"
1922
					+ e.getMessage());
1923
			e.printStackTrace(System.out);
1924
			if (printFlag) {
1925
				try {
1926
					out.write("<error>" + e.getMessage() + "</error>");
1927
				} catch (IOException e1) {
1928
					logMetacat.error(e1.getMessage(), e1);
1929
				}
1930
			}
1931
		} finally {
1932
			try {
1933
				pstmt.close();
1934
			}//try
1935
			catch (SQLException ee) {
1936
				logMetacat.error("ReplicationService.handleGetCatalogRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
1937
				logReplication.error("ReplicationService.handleGetCatalogRequest - Error in MetacatReplication.handleGetCatalogRequest: "
1938
						+ ee.getMessage());
1939
			}//catch
1940
			finally {
1941
				DBConnectionPool.returnDBConnection(dbConn, serialNumber);
1942
			}//finally
1943
			if (out != null) {
1944
				try {
1945
					out.close();
1946
				} catch (IOException e1) {
1947
					logMetacat.error(e1.getMessage(), e1);
1948
				}
1949
			}
1950
		}//finally
1951

    
1952
		return null;
1953
	}
1954

    
1955
	/**
1956
	 * Sends the current system date to the remote server.  Using this action
1957
	 * for replication gets rid of any problems with syncronizing clocks
1958
	 * because a time specific to a document is always kept on its home server.
1959
	 */
1960
	protected static void handleGetTimeRequest(
1961
			Hashtable<String, String[]> params, HttpServletResponse response) {
1962
		
1963
		// use standard format -- the receiving end wants this too
1964
		String dateString = DateTimeMarshaller.serializeDateToUTC(Calendar.getInstance().getTime());
1965
		
1966
		// get a writer for sending back to response
1967
		response.setContentType("text/xml");
1968
		Writer out = null;
1969
		try {
1970
			out = response.getWriter();
1971
			out.write("<timestamp>" + dateString + "</timestamp>");
1972
			out.close();
1973
		} catch (IOException e) {
1974
			logMetacat.error(e.getMessage(), e);
1975
		}
1976
		
1977
	}
1978

    
1979
	/**
1980
	 * this method handles the timeout for a file lock.  when a lock is
1981
	 * granted it is granted for 30 seconds.  When this thread runs out
1982
	 * it deletes the docid from the queue, thus eliminating the lock.
1983
	 */
1984
	public void run() {
1985
		try {
1986
			logReplication.info("ReplicationService.run - thread started for docid: "
1987
					+ (String) fileLocks.elementAt(0));
1988

    
1989
			Thread.sleep(30000); //the lock will expire in 30 seconds
1990
			logReplication.info("thread for docid: "
1991
					+ (String) fileLocks.elementAt(fileLocks.size() - 1) + " exiting.");
1992

    
1993
			fileLocks.remove(fileLocks.size() - 1);
1994
			//fileLocks is treated as a FIFO queue.  If there are more than one lock
1995
			//in the vector, the first one inserted will be removed.
1996
		} catch (Exception e) {
1997
			logMetacat.error("ReplicationService.run - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
1998
			logReplication.error("ReplicationService.run - error in file lock thread from "
1999
					+ "MetacatReplication.run: " + e.getMessage());
2000
		}
2001
	}
2002

    
2003
	/**
2004
	 * Returns the name of a server given a serverCode
2005
	 * @param serverCode the serverid of the server
2006
	 * @return the servername or null if the specified serverCode does not
2007
	 *         exist.
2008
	 */
2009
	public static String getServerNameForServerCode(int serverCode) {
2010
		//System.out.println("serverid: " + serverCode);
2011
		DBConnection dbConn = null;
2012
		int serialNumber = -1;
2013
		PreparedStatement pstmt = null;
2014
		try {
2015
			dbConn = DBConnectionPool.getDBConnection("MetacatReplication.getServer");
2016
			serialNumber = dbConn.getCheckOutSerialNumber();
2017
			String sql = new String("select server from "
2018
					+ "xml_replication where serverid = ?");
2019
			pstmt = dbConn.prepareStatement(sql);
2020
			pstmt.setInt(1, serverCode);
2021
			//System.out.println("getserver sql: " + sql);
2022
			pstmt.execute();
2023
			ResultSet rs = pstmt.getResultSet();
2024
			boolean tablehasrows = rs.next();
2025
			if (tablehasrows) {
2026
				//System.out.println("server: " + rs.getString(1));
2027
				return rs.getString(1);
2028
			}
2029

    
2030
			//conn.close();
2031
		} catch (Exception e) {
2032
			logMetacat.error("ReplicationService.getServerNameForServerCode - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
2033
			logReplication.error("ReplicationService.getServerNameForServerCode - Error in MetacatReplication.getServer: " + e.getMessage());
2034
		} finally {
2035
			try {
2036
				pstmt.close();
2037
			}//try
2038
			catch (SQLException ee) {
2039
				logMetacat.error("ReplicationService.getServerNameForServerCode - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
2040
				logReplication.error("ReplicationService.getServerNameForServerCode - Error in MetacactReplication.getserver: "
2041
						+ ee.getMessage());
2042
			}//catch
2043
			finally {
2044
				DBConnectionPool.returnDBConnection(dbConn, serialNumber);
2045
			}//fianlly
2046
		}//finally
2047

    
2048
		return null;
2049
		//return null if the server does not exist
2050
	}
2051

    
2052
	/**
2053
	 * Returns a server code given a server name
2054
	 * @param server the name of the server
2055
	 * @return integer > 0 representing the code of the server, 0 if the server
2056
	 *  does not exist.
2057
	 */
2058
	public static int getServerCodeForServerName(String server) throws ServiceException {
2059
		DBConnection dbConn = null;
2060
		int serialNumber = -1;
2061
		PreparedStatement pstmt = null;
2062
		int serverCode = 0;
2063

    
2064
		try {
2065

    
2066
			//conn = util.openDBConnection();
2067
			dbConn = DBConnectionPool.getDBConnection("MetacatReplication.getServerCode");
2068
			serialNumber = dbConn.getCheckOutSerialNumber();
2069
			pstmt = dbConn.prepareStatement("SELECT serverid FROM xml_replication "
2070
					+ "WHERE server LIKE ?");
2071
			pstmt.setString(1, server);
2072
			pstmt.execute();
2073
			ResultSet rs = pstmt.getResultSet();
2074
			boolean tablehasrows = rs.next();
2075
			if (tablehasrows) {
2076
				serverCode = rs.getInt(1);
2077
				pstmt.close();
2078
				//conn.close();
2079
				return serverCode;
2080
			}
2081

    
2082
		} catch (SQLException sqle) {
2083
			throw new ServiceException("ReplicationService.getServerCodeForServerName - " 
2084
					+ "SQL error when getting server code: " + sqle.getMessage());
2085

    
2086
		} finally {
2087
			try {
2088
				pstmt.close();
2089
				//conn.close();
2090
			}//try
2091
			catch (Exception ee) {
2092
				logMetacat.error("ReplicationService.getServerCodeForServerName - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
2093
				logReplication.error("ReplicationService.getServerNameForServerCode - Error in MetacatReplicatio.getServerCode: "
2094
						+ ee.getMessage());
2095

    
2096
			}//catch
2097
			finally {
2098
				DBConnectionPool.returnDBConnection(dbConn, serialNumber);
2099
			}//finally
2100
		}//finally
2101

    
2102
		return serverCode;
2103
	}
2104
	
2105
	/**
2106
	 * Returns a Map of serverCode=serverName 
2107
	 * @return Map of server codes to names (URIs)
2108
	 */
2109
	public static Map<Integer, String> getServerCodes() throws ServiceException {
2110
		DBConnection dbConn = null;
2111
		int serialNumber = -1;
2112
		PreparedStatement pstmt = null;
2113
		
2114
		Map<Integer, String> codes = new HashMap<Integer, String>();
2115

    
2116
		try {
2117

    
2118
			dbConn = DBConnectionPool.getDBConnection("MetacatReplication.getServerCodes");
2119
			serialNumber = dbConn.getCheckOutSerialNumber();
2120
			pstmt = dbConn.prepareStatement("SELECT serverid, server FROM xml_replication ");
2121
			pstmt.execute();
2122
			ResultSet rs = pstmt.getResultSet();
2123
			while (rs.next()) {
2124
				int serverCode = rs.getInt(1);
2125
				String server = rs.getString(2);
2126
				codes.put(serverCode, server);
2127
			}
2128
			pstmt.close();
2129
			
2130
		} catch (SQLException sqle) {
2131
			throw new ServiceException("ReplicationService.getServerCodes - " 
2132
					+ "SQL error when getting server map: " + sqle.getMessage());
2133

    
2134
		} finally {
2135
			try {
2136
				pstmt.close();
2137
			}//try
2138
			catch (Exception ee) {
2139
				logMetacat.error("ReplicationService.getServerCodes - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
2140
				logReplication.error("ReplicationService.getServerCodes - Error in MetacatReplicatio.getServerCodes: "
2141
						+ ee.getMessage());
2142

    
2143
			}//catch
2144
			finally {
2145
				DBConnectionPool.returnDBConnection(dbConn, serialNumber);
2146
			}//finally
2147
		}//finally
2148

    
2149
		return codes;
2150
	}
2151

    
2152
	/**
2153
	 * Method to get a host server information for given docid
2154
	 * @param conn a connection to the database
2155
	 */
2156
	public static Hashtable<String, String> getHomeServerInfoForDocId(String docId) {
2157
		Hashtable<String, String> sl = new Hashtable<String, String>();
2158
		DBConnection dbConn = null;
2159
		int serialNumber = -1;
2160
		docId = DocumentUtil.getDocIdFromString(docId);
2161
		PreparedStatement pstmt = null;
2162
		int serverLocation;
2163
		try {
2164
			//get conection
2165
			dbConn = DBConnectionPool.getDBConnection("ReplicationHandler.getHomeServer");
2166
			serialNumber = dbConn.getCheckOutSerialNumber();
2167
			//get a server location from xml_document table
2168
			pstmt = dbConn.prepareStatement("select server_location from xml_documents "
2169
					+ "where docid = ?");
2170
			pstmt.setString(1, docId);
2171
			pstmt.execute();
2172
			ResultSet serverName = pstmt.getResultSet();
2173
			//get a server location
2174
			if (serverName.next()) {
2175
				serverLocation = serverName.getInt(1);
2176
				pstmt.close();
2177
			} else {
2178
				pstmt.close();
2179
				//ut.returnConnection(conn);
2180
				return null;
2181
			}
2182
			pstmt = dbConn.prepareStatement("select server, last_checked, replicate "
2183
					+ "from xml_replication where serverid = ?");
2184
			//increase usage count
2185
			dbConn.increaseUsageCount(1);
2186
			pstmt.setInt(1, serverLocation);
2187
			pstmt.execute();
2188
			ResultSet rs = pstmt.getResultSet();
2189
			boolean tableHasRows = rs.next();
2190
			if (tableHasRows) {
2191

    
2192
				String server = rs.getString(1);
2193
				String last_checked = rs.getString(2);
2194
				if (!server.equals("localhost")) {
2195
					sl.put(server, last_checked);
2196
				}
2197

    
2198
			} else {
2199
				pstmt.close();
2200
				//ut.returnConnection(conn);
2201
				return null;
2202
			}
2203
			pstmt.close();
2204
		} catch (Exception e) {
2205
			logMetacat.error("ReplicationService.getHomeServerInfoForDocId - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
2206
			logReplication.error("ReplicationService.getHomeServerInfoForDocId - error in replicationHandler.getHomeServer(): "
2207
					+ e.getMessage());
2208
		} finally {
2209
			try {
2210
				pstmt.close();
2211
				//ut.returnConnection(conn);
2212
			} catch (Exception ee) {
2213
				logMetacat.error("ReplicationService.getHomeServerInfoForDocId - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
2214
				logReplication.error("ReplicationService.getHomeServerInfoForDocId - Eror irn rplicationHandler.getHomeServer() "
2215
						+ "to close pstmt: " + ee.getMessage());
2216
			} finally {
2217
				DBConnectionPool.returnDBConnection(dbConn, serialNumber);
2218
			}
2219

    
2220
		}//finally
2221
		return sl;
2222
	}
2223

    
2224
	/**
2225
	 * Returns a home server location  given a accnum
2226
	 * @param accNum , given accNum for a document
2227
	 *
2228
	 */
2229
	public static int getHomeServerCodeForDocId(String accNum) throws ServiceException {
2230
		DBConnection dbConn = null;
2231
		int serialNumber = -1;
2232
		PreparedStatement pstmt = null;
2233
		int serverCode = 1;
2234
		String docId = DocumentUtil.getDocIdFromString(accNum);
2235

    
2236
		try {
2237

    
2238
			// Get DBConnection
2239
			dbConn = DBConnectionPool
2240
					.getDBConnection("ReplicationHandler.getServerLocation");
2241
			serialNumber = dbConn.getCheckOutSerialNumber();
2242
			pstmt = dbConn.prepareStatement("SELECT server_location FROM xml_documents "
2243
					+ "WHERE docid LIKE ? ");
2244
			pstmt.setString(1, docId);
2245
			pstmt.execute();
2246
			ResultSet rs = pstmt.getResultSet();
2247
			boolean tablehasrows = rs.next();
2248
			//If a document is find, return the server location for it
2249
			if (tablehasrows) {
2250
				serverCode = rs.getInt(1);
2251
				pstmt.close();
2252
				//conn.close();
2253
				return serverCode;
2254
			}
2255
			//if couldn't find in xml_documents table, we think server code is 1
2256
			//(this is new document)
2257
			else {
2258
				pstmt.close();
2259
				//conn.close();
2260
				return serverCode;
2261
			}
2262

    
2263
		} catch (SQLException sqle) {
2264
			throw new ServiceException("ReplicationService.getHomeServerCodeForDocId - " 
2265
					+ "SQL error when getting home server code for docid: " + docId + " : " 
2266
					+ sqle.getMessage());
2267

    
2268
		} finally {
2269
			try {
2270
				pstmt.close();
2271
				//conn.close();
2272

    
2273
			} catch (SQLException sqle) {
2274
				logMetacat.error("ReplicationService.getHomeServerCodeForDocId - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
2275
				logReplication.error("ReplicationService.getHomeServerCodeForDocId - ReplicationService.getHomeServerCodeForDocId - " 
2276
						+ "SQL error when getting home server code for docid: " + docId + " : " 
2277
						+ sqle.getMessage());
2278
			} finally {
2279
				DBConnectionPool.returnDBConnection(dbConn, serialNumber);
2280
			}//finally
2281
		}//finally
2282
		//return serverCode;
2283
	}
2284

    
2285
	/**
2286
	 * This method returns the content of a url
2287
	 * @param u the url to return the content from
2288
	 * @return a string representing the content of the url
2289
	 * @throws java.io.IOException
2290
	 */
2291
	public static String getURLContent(URL u) throws Exception {
2292
		char istreamChar;
2293
		int istreamInt;
2294
		// get the response content
2295
		InputStream input = null;
2296
		String content = null;
2297
		try {
2298
		    input = getURLStream(u);
2299
		    logReplication.info("ReplicationService.getURLContent - After getting response from: " + u.toString());
2300
		    content = IOUtils.toString(input, "UTF-8");
2301
		} 
2302
		finally {
2303
		    IOUtils.closeQuietly(input);
2304
		}
2305
        return content;
2306
	}
2307
	
2308
	/**
2309
	 * This method returns the InputStream after opening a url
2310
	 * @param u the url to return the content from
2311
	 * @return a InputStream representing the content of the url
2312
	 * @throws java.io.IOException
2313
	 */
2314
	public static InputStream getURLStream(URL u) throws Exception {
2315
	    logReplication.info("Getting url stream from " + u.toString());
2316
	    logReplication.info("ReplicationService.getURLStream - Before sending request to: " + u.toString());
2317
	    // use httpclient to set up SSL
2318

    
2319
	    InputStream input = null;
2320
	    try {
2321
	        RestClient client = getSSLClient();
2322
	        HttpResponse response = client.doGetRequest(u.toString(),null);
2323
	        // get the response content
2324
	        StatusLine statusLine = response.getStatusLine();
2325
	        HttpEntity entity = response.getEntity();
2326
	        logReplication.info("ReplicationService.getURLStream - After getting response from: " + u.toString());
2327
	        if (statusLine.getStatusCode() >= 300) {
2328
	            throw new HttpResponseException(
2329
	                    statusLine.getStatusCode(),
2330
	                    "ReplicationService.getURLStream - " + statusLine.getReasonPhrase());
2331
	        }
2332
	        if (entity == null) {
2333
	            throw new ClientProtocolException("ReplicationService.getURLStream - Response contains no content");
2334
	        }
2335
	        input = entity.getContent();
2336
	    } 
2337
	    catch (Throwable t) {
2338
	        logReplication.error("Unexpected Throwable encountered.  Logging and moving on: " +
2339
	                t.getClass().getCanonicalName() + ": " + t.getMessage());
2340
	        logReplication.error(ExceptionUtils.getStackTrace(t));
2341
	        throw new Exception(t);
2342
	    }
2343
		return input;
2344
	}
2345
	
2346
	/**
2347
     * This method returns a byte array after opening a url
2348
     * @param u the url to return the content from
2349
     * @return a InputStream representing the content of the url
2350
     * @throws java.io.IOException
2351
     */
2352
    public static byte[] getURLBytes(URL u) throws Exception {
2353
        InputStream input = null;
2354
        try {
2355
            input = getURLStream(u);
2356
            byte[] bytes = IOUtils.toByteArray(input);
2357
            return bytes;
2358
        } 
2359
        finally {
2360
            IOUtils.closeQuietly(input);
2361
        }
2362
    }
2363
	
2364
	/**
2365
	 * Sets up an HttpClient with SSL connection.
2366
	 * Sends client certificate to the server when doing the request.
2367
	 * @return
2368
	 */
2369
	private static RestClient getSSLClient() {
2370
	    RestClient sslClient = null;
2371
	    if (sslClient == null) {
2372
		
2373
	        // set up this server's client identity
2374
	        String subject = null;
2375
	        try {
2376
	            // TODO: should there be alternative ways to get the key and certificate?
2377
	            String certificateFile = PropertyService.getProperty("replication.certificate.file");
2378
	            String keyFile = PropertyService.getProperty("replication.privatekey.file");
2379
	            String keyPassword = PropertyService.getProperty("replication.privatekey.password");
2380
	            X509Certificate certificate = CertificateManager.getInstance().loadCertificateFromFile(certificateFile);
2381
	            PrivateKey privateKey = CertificateManager.getInstance().loadPrivateKeyFromFile(keyFile, keyPassword);
2382
	            subject = CertificateManager.getInstance().getSubjectDN(certificate);
2383
	            CertificateManager.getInstance().registerCertificate(subject, certificate, privateKey);
2384
	        } catch (Exception e) {
2385
	            // this is pretty much required for replication communication
2386
	            logReplication.warn("Could not find server's client certificate/private key: " + e.getMessage());
2387
	        }
2388

    
2389
	        try {
2390
	            RequestConfig rc = RequestConfig.custom()
2391
	                    .setConnectionRequestTimeout(CLIENTTIMEOUT)
2392
	                    .setConnectTimeout(CLIENTTIMEOUT)
2393
	                    .setSocketTimeout(CLIENTTIMEOUT).build();
2394
	            HttpClient hc = HttpUtils.getHttpClientBuilder(HttpUtils.selectSession(subject))
2395
	                    .setDefaultRequestConfig(rc)
2396
	                    .build();
2397
	            
2398
	            sslClient = new RestClient(hc);
2399
	        } 
2400
	        catch (FileNotFoundException e) {
2401
	            // these are somewhat expected for anonymous client use
2402
	            logReplication.warn("Could not set up SSL connection for client - likely because the certificate could not be located: " + e.getMessage());
2403
	        }
2404
	        catch (Exception e) {
2405
	            // this is likely more severe
2406
	            logReplication.error("Failed to set up SSL connection for client. Continuing. " + e.getClass() + ":: " + e.getMessage(), e);
2407
	        }
2408
	    }
2409
		return sslClient;
2410
	}
2411

    
2412
	
2413

    
2414
//	/**
2415
//	 * Method for writing replication messages to a log file specified in
2416
//	 * metacat.properties
2417
//	 */
2418
//	public static void replLog(String message) {
2419
//		try {
2420
//			FileOutputStream fos = new FileOutputStream(PropertyService
2421
//					.getProperty("replication.logdir")
2422
//					+ "/metacatreplication.log", true);
2423
//			PrintWriter pw = new PrintWriter(fos);
2424
//			SimpleDateFormat formatter = new SimpleDateFormat("yy-MM-dd HH:mm:ss");
2425
//			java.util.Date localtime = new java.util.Date();
2426
//			String dateString = formatter.format(localtime);
2427
//			dateString += " :: " + message;
2428
//			// time stamp each entry
2429
//			pw.println(dateString);
2430
//			pw.flush();
2431
//		} catch (Exception e) {
2432
//			logReplication.error("error writing to replication log from "
2433
//					+ "MetacatReplication.replLog: " + e.getMessage());
2434
//			// e.printStackTrace(System.out);
2435
//		}
2436
//	}
2437

    
2438
//	/**
2439
//	 * Method for writing replication messages to a log file specified in
2440
//	 * metacat.properties
2441
//	 */
2442
//	public static void replErrorLog(String message) {
2443
//		try {
2444
//			FileOutputStream fos = new FileOutputStream(PropertyService
2445
//					.getProperty("replication.logdir")
2446
//					+ "/metacatreplicationerror.log", true);
2447
//			PrintWriter pw = new PrintWriter(fos);
2448
//			SimpleDateFormat formatter = new SimpleDateFormat("yy-MM-dd HH:mm:ss");
2449
//			java.util.Date localtime = new java.util.Date();
2450
//			String dateString = formatter.format(localtime);
2451
//			dateString += " :: " + message;
2452
//			//time stamp each entry
2453
//			pw.println(dateString);
2454
//			pw.flush();
2455
//		} catch (Exception e) {
2456
//			logReplication.error("error writing to replication error log from "
2457
//					+ "MetacatReplication.replErrorLog: " + e.getMessage());
2458
//			//e.printStackTrace(System.out);
2459
//		}
2460
//	}
2461

    
2462
	/**
2463
	 * Returns true if the replicate field for server in xml_replication is 1.
2464
	 * Returns false otherwise
2465
	 */
2466
	public static boolean replToServer(String server) {
2467
		DBConnection dbConn = null;
2468
		int serialNumber = -1;
2469
		PreparedStatement pstmt = null;
2470
		try {
2471
			dbConn = DBConnectionPool.getDBConnection("MetacatReplication.repltoServer");
2472
			serialNumber = dbConn.getCheckOutSerialNumber();
2473
			pstmt = dbConn.prepareStatement("select replicate from "
2474
					+ "xml_replication where server like ? ");
2475
			pstmt.setString(1, server);
2476
			pstmt.execute();
2477
			ResultSet rs = pstmt.getResultSet();
2478
			boolean tablehasrows = rs.next();
2479
			if (tablehasrows) {
2480
				int i = rs.getInt(1);
2481
				if (i == 1) {
2482
					pstmt.close();
2483
					//conn.close();
2484
					return true;
2485
				} else {
2486
					pstmt.close();
2487
					//conn.close();
2488
					return false;
2489
				}
2490
			}
2491
		} catch (SQLException sqle) {
2492
			logMetacat.error("ReplicationService.replToServer - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
2493
			logReplication.error("ReplicationService.replToServer - SQL error in MetacatReplication.replToServer: "
2494
					+ sqle.getMessage());
2495
		} finally {
2496
			try {
2497
				pstmt.close();
2498
				//conn.close();
2499
			}//try
2500
			catch (Exception ee) {
2501
				logMetacat.error("ReplicationService.replToServer - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
2502
				logReplication.error("ReplicationService.replToServer - Error in MetacatReplication.replToServer: "
2503
						+ ee.getMessage());
2504
			}//catch
2505
			finally {
2506
				DBConnectionPool.returnDBConnection(dbConn, serialNumber);
2507
			}//finally
2508
		}//finally
2509
		return false;
2510
		//the default if this server does not exist is to not replicate to it.
2511
	}
2512

    
2513
}
(6-6/7)