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: leinfelder $'
9
 *     '$Date: 2014-02-10 11:52:33 -0800 (Mon, 10 Feb 2014) $'
10
 * '$Revision: 8585 $'
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.InputStreamReader;
38
import java.io.OutputStream;
39
import java.io.StringReader;
40
import java.io.Writer;
41
import java.net.MalformedURLException;
42
import java.net.URL;
43
import java.security.PrivateKey;
44
import java.security.cert.X509Certificate;
45
import java.sql.PreparedStatement;
46
import java.sql.ResultSet;
47
import java.sql.SQLException;
48
import java.sql.Timestamp;
49
import java.util.ArrayList;
50
import java.util.Arrays;
51
import java.util.Calendar;
52
import java.util.Date;
53
import java.util.Hashtable;
54
import java.util.List;
55
import java.util.Timer;
56
import java.util.Vector;
57

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

    
61
import org.apache.commons.io.IOUtils;
62
import org.apache.http.HttpResponse;
63
import org.apache.http.conn.scheme.Scheme;
64
import org.apache.http.conn.ssl.SSLSocketFactory;
65
import org.apache.log4j.Logger;
66
import org.dataone.client.RestClient;
67
import org.dataone.client.auth.CertificateManager;
68
import org.dataone.service.types.v1.Identifier;
69
import org.dataone.service.types.v1.SystemMetadata;
70
import org.dataone.service.util.DateTimeMarshaller;
71
import org.dataone.service.util.TypeMarshaller;
72
import org.jibx.runtime.JiBXException;
73
import org.xml.sax.InputSource;
74
import org.xml.sax.SAXException;
75
import org.xml.sax.XMLReader;
76

    
77
import edu.ucsb.nceas.metacat.DocumentImpl;
78
import edu.ucsb.nceas.metacat.DocumentImplWrapper;
79
import edu.ucsb.nceas.metacat.EventLog;
80
import edu.ucsb.nceas.metacat.IdentifierManager;
81
import edu.ucsb.nceas.metacat.McdbDocNotFoundException;
82
import edu.ucsb.nceas.metacat.McdbException;
83
import edu.ucsb.nceas.metacat.accesscontrol.AccessControlException;
84
import edu.ucsb.nceas.metacat.accesscontrol.AccessControlForSingleFile;
85
import edu.ucsb.nceas.metacat.accesscontrol.PermOrderException;
86
import edu.ucsb.nceas.metacat.admin.upgrade.RemoveInvalidReplicas;
87
import edu.ucsb.nceas.metacat.admin.upgrade.dataone.GenerateORE;
88
import edu.ucsb.nceas.metacat.admin.upgrade.dataone.GenerateSystemMetadata;
89
import edu.ucsb.nceas.metacat.client.InsufficientKarmaException;
90
import edu.ucsb.nceas.metacat.database.DBConnection;
91
import edu.ucsb.nceas.metacat.database.DBConnectionPool;
92
import edu.ucsb.nceas.metacat.database.DatabaseService;
93
import edu.ucsb.nceas.metacat.dataone.SyncAccessPolicy;
94
import edu.ucsb.nceas.metacat.dataone.hazelcast.HazelcastService;
95
import edu.ucsb.nceas.metacat.index.MetacatSolrIndex;
96
import edu.ucsb.nceas.metacat.properties.PropertyService;
97
import edu.ucsb.nceas.metacat.shared.BaseService;
98
import edu.ucsb.nceas.metacat.shared.HandlerException;
99
import edu.ucsb.nceas.metacat.shared.ServiceException;
100
import edu.ucsb.nceas.metacat.util.DocumentUtil;
101
import edu.ucsb.nceas.metacat.util.MetacatUtil;
102
import edu.ucsb.nceas.metacat.util.ReplicationUtil;
103
import edu.ucsb.nceas.metacat.util.SystemUtil;
104
import edu.ucsb.nceas.utilities.FileUtil;
105
import edu.ucsb.nceas.utilities.GeneralPropertyException;
106
import edu.ucsb.nceas.utilities.PropertyNotFoundException;
107
import edu.ucsb.nceas.utilities.access.DocInfoHandler;
108
import edu.ucsb.nceas.utilities.access.XMLAccessDAO;
109

    
110
public class ReplicationService extends BaseService {
111

    
112
	private static ReplicationService replicationService = null;
113

    
114
	private long timeInterval;
115
	private Date firstTime;
116
	private boolean timedReplicationIsOn = false;
117
	Timer replicationDaemon;
118
	private static Vector<String> fileLocks = new Vector<String>();
119
//	private Thread lockThread = null;
120
	public static final String FORCEREPLICATEDELETE = "forcereplicatedelete";
121
	public static final String FORCEREPLICATEDELETEALL = "forcereplicatedeleteall";
122
	private static String TIMEREPLICATION = "replication.timedreplication";
123
	private static String TIMEREPLICATIONINTERVAl ="replication.timedreplicationinterval";
124
	private static String FIRSTTIME = "replication.firsttimedreplication";
125
	private static final int TIMEINTERVALLIMIT = 7200000;
126
	public static final String REPLICATIONUSER = "replication";
127
	
128
	private static int CLIENTTIMEOUT = 30000;
129

    
130
	public static final String REPLICATION_LOG_FILE_NAME = "metacatreplication.log";
131

    
132
	private static String DATA_FILE_FLAG = null;
133
	public static String METACAT_REPL_ERROR_MSG = null;
134
	private static Logger logReplication = Logger.getLogger("ReplicationLogging");
135
	private static Logger logMetacat = Logger.getLogger(ReplicationService.class);
136

    
137
	static {
138
		// lookup the client timeout
139
		String clientTimeout = null;
140
		try {
141
			clientTimeout = PropertyService.getProperty("replication.client.timeout");
142
			CLIENTTIMEOUT = Integer.parseInt(clientTimeout);
143
		} catch (Exception e) {
144
			// just use default
145
			logReplication.warn("No custom client timeout specified in configuration, using default." + e.getMessage());
146
		}
147
		try {
148
			DATA_FILE_FLAG = PropertyService.getProperty("replication.datafileflag");
149
		} catch (PropertyNotFoundException e) {
150
			logReplication.error("No 'replication.datafileflag' specified in configuration." + e.getMessage());
151
		}
152

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

    
179
			String timeIntervalStr = 
180
				PropertyService.getProperty("replication.timedreplicationinterval");
181
			timeInterval = (new Long(timeIntervalStr)).longValue();
182
			logReplication.info("ReplicationService.initialize - The timed replication time Interval is " + timeInterval);
183

    
184
			String firstTimeStr = 
185
				PropertyService.getProperty("replication.firsttimedreplication");
186
			logReplication.info("ReplicationService.initialize - first replication time form property is " + firstTimeStr);
187
			firstTime = ReplicationHandler.combinateCurrentDateAndGivenTime(firstTimeStr);
188

    
189
			logReplication.info("ReplicationService.initialize - After combine current time, the real first time is "
190
					+ firstTime.toString() + " minisec");
191

    
192
			// set up time replication if it is on
193
			if (timedReplicationIsOn) {
194
				replicationDaemon.scheduleAtFixedRate(new ReplicationHandler(),
195
						firstTime, timeInterval);
196
				logReplication.info("ReplicationService.initialize - deltaT handler started with rate="
197
						+ timeInterval + " mini seconds at " + firstTime.toString());
198
			}
199

    
200
		} catch (PropertyNotFoundException pnfe) {
201
			throw new ServiceException(
202
					"ReplicationService.initialize - Property error while instantiating "
203
							+ "replication service: " + pnfe.getMessage());
204
		} catch (HandlerException he) {
205
			throw new ServiceException(
206
					"ReplicationService.initialize - Handler error while instantiating "
207
							+ "replication service" + he.getMessage());
208
		} 
209
	}
210

    
211
	/**
212
	 * Get the single instance of SessionService.
213
	 * 
214
	 * @return the single instance of SessionService
215
	 */
216
	public static ReplicationService getInstance() throws ServiceException {
217
		if (replicationService == null) {
218
			replicationService = new ReplicationService();
219
		}
220
		return replicationService;
221
	}
222

    
223
	public boolean refreshable() {
224
		return true;
225
	}
226

    
227
	protected void doRefresh() throws ServiceException {
228
		return;
229
	}
230
	
231
	public void stop() throws ServiceException{
232
		
233
	}
234

    
235
	public void stopReplication() throws ServiceException {
236
	      //stop the replication server
237
	      replicationDaemon.cancel();
238
	      replicationDaemon = new Timer(true);
239
	      timedReplicationIsOn = false;
240
	      try {
241
	    	  PropertyService.setProperty("replication.timedreplication", (new Boolean(timedReplicationIsOn)).toString());
242
	      } catch (GeneralPropertyException gpe) {
243
	    	  logReplication.warn("ReplicationService.stopReplication - Could not set replication.timedreplication property: " + gpe.getMessage());
244
	      }
245

    
246
	      logReplication.info("ReplicationService.stopReplication - deltaT handler stopped");
247
		return;
248
	}
249
	
250
	public void startReplication(Hashtable<String, String[]> params) throws ServiceException {
251

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

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

    
346
			// add server to server list
347
			if (subaction.equals("add")) {
348
				replicate = ((String[]) params.get("replicate"))[0];
349
				server = ((String[]) params.get("server"))[0];
350

    
351
				//Get data replication value
352
				dataReplicate = ((String[]) params.get("datareplicate"))[0];
353
				
354
				//Get hub value
355
				hub = ((String[]) params.get("hub"))[0];
356

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

    
396
			} else if (subaction.equals("generatesystemmetadata")) {
397
				GenerateSystemMetadata gsm = new GenerateSystemMetadata();
398
				int serverLocation = -1;
399
				String serverid = ((String[]) params.get("serverid"))[0];
400
				serverLocation = Integer.parseInt(serverid);
401
				gsm.setServerLocation(serverLocation );
402
				gsm.multiThreadUpgrade();
403
				out.write("System Metadata generated for server " + serverid);
404
				
405
			} else if (subaction.equals("generateore")) {
406
				GenerateORE gore = new GenerateORE();
407
				int serverLocation = -1;
408
				String serverid = ((String[]) params.get("serverid"))[0];
409
				serverLocation = Integer.parseInt(serverid);
410
				gore.setServerLocation(serverLocation );
411
				gore.upgrade();
412
				out.write("Generated ORE maps for server " + serverid);
413
				
414
			} else if (subaction.equals("removeinvalidreplicas")) {
415
				RemoveInvalidReplicas rir = new RemoveInvalidReplicas();
416
				int serverLocation = -1;
417
				String serverid = ((String[]) params.get("serverid"))[0];
418
				serverLocation = Integer.parseInt(serverid);
419
				rir.setServerLocation(serverLocation );
420
				rir.upgrade();
421
				out.write("Removed invalid replicas for server " + serverid);
422
				
423
			} else if (subaction.equals("syncaccesspolicy")) {
424
				SyncAccessPolicy syncAP = new SyncAccessPolicy();
425
				response.setContentType("text/html");
426
				out = response.getWriter();
427
				if (params.containsKey("pid")) {
428
					String[] pids = params.get("pid");
429
					logMetacat.debug("Attempting to sync access policies for pids: " + pids);
430
					ArrayList<String> pidsToSync = new ArrayList<String>(Arrays.asList(pids));
431
					try {
432
						List<Identifier> syncedPids = syncAP.sync(pidsToSync);
433
						out.write("<html><body>Syncing access policies has completed for " + syncedPids.size() + " pids.</body></html>");
434
					} catch (Exception e) {
435
						logMetacat.error("Error syncing all access polies: "
436
								+ e.getMessage());
437
						response.setContentType("text/html");
438
						out = response.getWriter();
439
						out.write("<html><body>Error syncing access policies</body></html>");
440
					}
441
				} else {
442
					logMetacat.debug("Syncing access policies for all docids for this node");
443
					try {
444
						syncAP.syncAll();
445
						out.write("<html><body>Syncing access policies for all docids has completed.</body></html>");
446
					} catch (Exception e) {
447
						logMetacat.error("Error syncing access policies: "
448
								+ e.getMessage());
449
						out.write("<html><body>Error syncing access policies: " + e.getMessage() + " </body></html>");
450
					}
451
				}
452
			} else {
453
			
454
				out.write("<error>Unkonwn subaction</error>");
455
				return;
456
			}
457
			
458
			// show SM generate button?
459
			String dataoneConfigured = PropertyService.getProperty("configutil.dataoneConfigured");
460
			if (dataoneConfigured != null) {
461
				showGenerateSystemMetadata = Boolean.parseBoolean(dataoneConfigured);
462
			}
463
			
464
			// always list them after processing
465
			response.setContentType("text/html");
466
			out.write("<html><body><table border=\"1\">");
467
			out.write("<tr><td><b>server</b></td>");
468
			out.write("<td><b>last_checked</b></td>");
469
			out.write("<td><b>replicate</b></td>");
470
			out.write("<td><b>datareplicate</b></td>");
471
			out.write("<td><b>hub</b></td>");
472
			if (showGenerateSystemMetadata) {
473
				out.write("<td><b>System Metadata</b></td>");
474
				out.write("<td><b>ORE Maps</b></td>");
475
				out.write("<td><b>Invalid Replicas</b></td>");
476
			}
477
			out.write("<td><b>Sync Access Policies</b></td>");
478
			out.write("</tr>");
479

    
480
			pstmt = dbConn.prepareStatement("SELECT serverid, server, last_checked, replicate, datareplicate, hub FROM xml_replication");
481
			pstmt.execute();
482
			ResultSet rs = pstmt.getResultSet();
483
			boolean tablehasrows = rs.next();
484
			while (tablehasrows) {
485
				String serverId = rs.getString(1);
486
				out.write("<tr><td>" + rs.getString(2) + "</td><td>");
487
				out.write(rs.getString(3) + "</td><td>");
488
				out.write(rs.getString(4) + "</td><td>");
489
				out.write(rs.getString(5) + "</td><td>");
490
				out.write(rs.getString(6) + "</td>");
491
				if (showGenerateSystemMetadata) {
492
					// for SM
493
					out.write("<td><form action='" + request.getContextPath() + "/admin'>");
494
					out.write("<input name='serverid' type='hidden' value='" + serverId  + "'/>");
495
					out.write("<input name='configureType' type='hidden' value='replication'/>");
496
					out.write("<input name='action' type='hidden' value='servercontrol'/>");
497
					out.write("<input name='subaction' type='hidden' value='generatesystemmetadata'/>");
498
					out.write("<input type='submit' value='Generate System Metadata'/>");
499
					out.write("</form></td>");
500
					
501
					// for ORE maps
502
					out.write("<td><form action='" + request.getContextPath() + "/admin'>");
503
					out.write("<input name='serverid' type='hidden' value='" + serverId + "'/>");
504
					out.write("<input name='configureType' type='hidden' value='replication'/>");
505
					out.write("<input name='action' type='hidden' value='servercontrol'/>");
506
					out.write("<input name='subaction' type='hidden' value='generateore'/>");
507
					out.write("<input type='submit' value='Generate ORE'/>");
508
					out.write("</form></td>");
509
					
510
					// for invalid replicas
511
					out.write("<td><form action='" + request.getContextPath() + "/admin'>");
512
					out.write("<input name='serverid' type='hidden' value='" + serverId + "'/>");
513
					out.write("<input name='configureType' type='hidden' value='replication'/>");
514
					out.write("<input name='action' type='hidden' value='servercontrol'/>");
515
					out.write("<input name='subaction' type='hidden' value='removeinvalidreplicas'/>");
516
					String disabled = "";
517
					if (Integer.valueOf(serverId) == 1) {
518
						disabled = "disabled='true'";
519
					}
520
					out.write("<input type='submit' value='Remove Invalid Replicas' " + disabled + " />");
521
					out.write("</form></td>");
522
				}
523
				// for syncing access policies (MN -> CN)
524
				out.write("<td><form action='" + request.getContextPath() + "/admin'>");
525
				out.write("<input name='serverid' type='hidden' value='" + serverId + "'/>");
526
				out.write("<input name='configureType' type='hidden' value='replication'/>");
527
				out.write("<input name='action' type='hidden' value='servercontrol'/>");
528
				out.write("<input name='subaction' type='hidden' value='syncaccesspolicy'/>");
529
				out.write("<input type='submit' value='Sync access policies'/>");
530
				out.write("</form></td>");
531
				
532
				out.write("</tr>");
533

    
534
				tablehasrows = rs.next();
535
			}
536
			out.write("</table></body></html>");
537
			
538
			
539
			pstmt.close();
540
			//conn.close();
541

    
542
		} catch (Exception e) {
543
			logMetacat.error("ReplicationService.handleServerControlRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
544
			logReplication.error("ReplicationService.handleServerControlRequest - Error in "
545
					+ "MetacatReplication.handleServerControlRequest " + e.getMessage());
546
			e.printStackTrace(System.out);
547
		} finally {
548
			try {
549
				pstmt.close();
550
			}//try
551
			catch (SQLException ee) {
552
				logMetacat.error("ReplicationService.handleServerControlRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
553
				logReplication.error("ReplicationService.handleServerControlRequest - Error in MetacatReplication.handleServerControlRequest to close pstmt "
554
						+ ee.getMessage());
555
			}//catch
556
			finally {
557
				DBConnectionPool.returnDBConnection(dbConn, serialNumber);
558
			}//finally
559
			if (out != null) {
560
				try {
561
					out.close();
562
				} catch (IOException e) {
563
					logMetacat.error(e.getMessage(), e);
564
				}
565
			}
566
		}//finally
567

    
568
	}
569

    
570
	/**
571
	 * when a forcereplication request comes in, local host sends a read request
572
	 * to the requesting server (remote server) for the specified docid. Then
573
	 * store it in local database.
574
	 */
575
	protected static void handleForceReplicateRequest(
576
			Hashtable<String, String[]> params, HttpServletResponse response,
577
			HttpServletRequest request) {
578
		String server = ((String[]) params.get("server"))[0]; // the server that
579
		String docid = ((String[]) params.get("docid"))[0]; // sent the document
580
		String dbaction = "UPDATE"; // the default action is UPDATE
581
		//    boolean override = false;
582
		//    int serverCode = 1;
583
		DBConnection dbConn = null;
584
		int serialNumber = -1;
585
		String docName = null;
586

    
587
		try {
588
			//if the url contains a dbaction then the default action is overridden
589
			if (params.containsKey("dbaction")) {
590
				dbaction = ((String[]) params.get("dbaction"))[0];
591
				//serverCode = MetacatReplication.getServerCode(server);
592
				//override = true; //we are now overriding the default action
593
			}
594
			logReplication.info("ReplicationService.handleForceReplicateRequest - Force replication request from: " + server);
595
			logReplication.info("ReplicationService.handleForceReplicateRequest - Force replication docid: " + docid);
596
			logReplication.info("ReplicationService.handleForceReplicateRequest - Force replication action: " + dbaction);
597
			// sending back read request to remote server
598
			URL u = new URL("https://" + server + "?server="
599
					+ MetacatUtil.getLocalReplicationServerName() + "&action=read&docid="
600
					+ docid);
601
			String xmldoc = ReplicationService.getURLContent(u);
602

    
603
			// get the document info from server
604
			URL docinfourl = new URL("https://" + server + "?server="
605
					+ MetacatUtil.getLocalReplicationServerName()
606
					+ "&action=getdocumentinfo&docid=" + docid);
607
			
608

    
609
			String docInfoStr = ReplicationService.getURLContent(docinfourl);
610
			// strip out the system metadata portion
611
			String systemMetadataXML = ReplicationUtil.getSystemMetadataContent(docInfoStr);
612
			docInfoStr = ReplicationUtil.getContentWithoutSystemMetadata(docInfoStr);
613
		   	  
614
			//dih is the parser for the docinfo xml format
615
			DocInfoHandler dih = new DocInfoHandler();
616
			XMLReader docinfoParser = ReplicationHandler.initParser(dih);
617
			docinfoParser.parse(new InputSource(new StringReader(docInfoStr)));
618
			//      Hashtable<String,Vector<AccessControlForSingleFile>> docinfoHash = dih.getDocInfo();
619
			Hashtable<String, String> docinfoHash = dih.getDocInfo();
620
			
621
			// Get home server of this docid
622
			String homeServer = (String) docinfoHash.get("home_server");
623
			
624
			// process system metadata
625
			if (systemMetadataXML != null) {
626
				SystemMetadata sysMeta = 
627
					TypeMarshaller.unmarshalTypeFromStream(
628
							SystemMetadata.class,
629
							new ByteArrayInputStream(systemMetadataXML.getBytes("UTF-8")));
630
				// need the guid-to-docid mapping
631
				boolean mappingExists = true;
632
		      	mappingExists = IdentifierManager.getInstance().mappingExists(sysMeta.getIdentifier().getValue());
633
		      	if (!mappingExists) {
634
		      		IdentifierManager.getInstance().createMapping(sysMeta.getIdentifier().getValue(), docid);
635
		      	}
636
				// save the system metadata
637
				HazelcastService.getInstance().getSystemMetadataMap().put(sysMeta.getIdentifier(), sysMeta);
638
				// submit for indexing
639
                MetacatSolrIndex.getInstance().submit(sysMeta.getIdentifier(), sysMeta, null);
640
			}
641
      
642
			// dates
643
			String createdDateString = docinfoHash.get("date_created");
644
			String updatedDateString = docinfoHash.get("date_updated");
645
			Date createdDate = DateTimeMarshaller.deserializeDateToUTC(createdDateString);
646
			Date updatedDate = DateTimeMarshaller.deserializeDateToUTC(updatedDateString);
647
		      
648
			logReplication.info("ReplicationService.handleForceReplicateRequest - homeServer: " + homeServer);
649
			// Get Document type
650
			String docType = (String) docinfoHash.get("doctype");
651
			logReplication.info("ReplicationService.handleForceReplicateRequest - docType: " + docType);
652
			String parserBase = null;
653
			// this for eml2 and we need user eml2 parser
654
			if (docType != null
655
					&& (docType.trim()).equals(DocumentImpl.EML2_0_0NAMESPACE)) {
656
				logReplication.warn("ReplicationService.handleForceReplicateRequest - This is an eml200 document!");
657
				parserBase = DocumentImpl.EML200;
658
			} else if (docType != null
659
					&& (docType.trim()).equals(DocumentImpl.EML2_0_1NAMESPACE)) {
660
				logReplication.warn("ReplicationService.handleForceReplicateRequest - This is an eml2.0.1 document!");
661
				parserBase = DocumentImpl.EML200;
662
			} else if (docType != null
663
					&& (docType.trim()).equals(DocumentImpl.EML2_1_0NAMESPACE)) {
664
				logReplication.warn("ReplicationService.handleForceReplicateRequest - This is an eml2.1.0 document!");
665
				parserBase = DocumentImpl.EML210;
666
			} else if (docType != null
667
					&& (docType.trim()).equals(DocumentImpl.EML2_1_1NAMESPACE)) {
668
				logReplication.warn("ReplicationService.handleForceReplicateRequest - This is an eml2.1.1 document!");
669
				parserBase = DocumentImpl.EML210;
670
			}
671
			logReplication.warn("ReplicationService.handleForceReplicateRequest - The parserBase is: " + parserBase);
672

    
673
			// Get DBConnection from pool
674
			dbConn = DBConnectionPool
675
					.getDBConnection("MetacatReplication.handleForceReplicateRequest");
676
			serialNumber = dbConn.getCheckOutSerialNumber();
677
			// write the document to local database
678
			DocumentImplWrapper wrapper = new DocumentImplWrapper(parserBase, false, false);
679
			//try this independently so we can set access even if the update action is invalid (i.e docid has not changed)
680
			try {
681
				wrapper.writeReplication(dbConn, xmldoc, null, null,
682
						dbaction, docid, null, null, homeServer, server, createdDate,
683
						updatedDate);
684
			} finally {
685

    
686
				//process extra access rules before dealing with the write exception (doc exist already)
687
				try {
688
		        	// check if we had a guid -> docid mapping
689
		        	String docidNoRev = DocumentUtil.getDocIdFromAccessionNumber(docid);
690
		        	int rev = DocumentUtil.getRevisionFromAccessionNumber(docid);
691
		        	IdentifierManager.getInstance().getGUID(docidNoRev, rev);
692
		        	// no need to create the mapping if we have it
693
		        } catch (McdbDocNotFoundException mcdbe) {
694
		        	// create mapping if we don't
695
		        	IdentifierManager.getInstance().createMapping(docid, docid);
696
		        }
697
		        Vector<XMLAccessDAO> accessControlList = dih.getAccessControlList();
698
		        if (accessControlList != null) {
699
		        	AccessControlForSingleFile acfsf = new AccessControlForSingleFile(docid);
700
		        	for (XMLAccessDAO xmlAccessDAO : accessControlList) {
701
		        		try {
702
			        		if (!acfsf.accessControlExists(xmlAccessDAO)) {
703
			        			acfsf.insertPermissions(xmlAccessDAO);
704
								logReplication.info("ReplicationService.handleForceReplicateRequest - document " + docid
705
										+ " permissions added to DB");
706
			        		}
707
		        		} catch (PermOrderException poe) {
708
		        			// this is problematic, but should not prevent us from replicating
709
		        			// see https://redmine.dataone.org/issues/2583
710
		        			String msg = "Could not insert access control for: " + docid + " Message: " + poe.getMessage();
711
		        			logMetacat.error(msg, poe);
712
		        			logReplication.error(msg, poe);
713
		        		}
714
		            }
715
		        }
716
		        
717
		        // process the real owner and updater
718
				String user = (String) docinfoHash.get("user_owner");
719
				String updated = (String) docinfoHash.get("user_updated");
720
		        updateUserOwner(dbConn, docid, user, updated);
721

    
722
				logReplication.info("ReplicationService.handleForceReplicateRequest - document " + docid + " added to DB with "
723
						+ "action " + dbaction);
724
				
725
				EventLog.getInstance().log(request.getRemoteAddr(), request.getHeader("User-Agent"), REPLICATIONUSER, docid, dbaction);
726
			}
727
		} catch (SQLException sqle) {
728
			logMetacat.error("ReplicationService.handleForceReplicateRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
729
			logReplication.error("ReplicationService.handleForceReplicateRequest - SQL error when adding doc " + docid + 
730
					" to DB with action " + dbaction + ": " + sqle.getMessage());
731
		} catch (MalformedURLException mue) {
732
			logMetacat.error("ReplicationService.handleForceReplicateRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
733
			logReplication.error("ReplicationService.handleForceReplicateRequest - URL error when adding doc " + docid + 
734
					" to DB with action " + dbaction + ": " + mue.getMessage());
735
		} catch (SAXException se) {
736
			logMetacat.error("ReplicationService.handleForceReplicateRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
737
			logReplication.error("ReplicationService.handleForceReplicateRequest - SAX parsing error when adding doc " + docid + 
738
					" to DB with action " + dbaction + ": " + se.getMessage());
739
		} catch (HandlerException he) {
740
			logMetacat.error("ReplicationService.handleForceReplicateRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
741
			logReplication.error("ReplicationService.handleForceReplicateRequest - Handler error when adding doc " + docid + 
742
					" to DB with action " + dbaction + ": " + he.getMessage());
743
		} catch (IOException ioe) {
744
			logMetacat.error("ReplicationService.handleForceReplicateRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
745
			logReplication.error("ReplicationService.handleForceReplicateRequest - I/O error when adding doc " + docid + 
746
					" to DB with action " + dbaction + ": " + ioe.getMessage());
747
		} catch (PermOrderException poe) {
748
			logMetacat.error("ReplicationService.handleForceReplicateRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
749
			logReplication.error("ReplicationService.handleForceReplicateRequest - Permissions order error when adding doc " + docid + 
750
					" to DB with action " + dbaction + ": " + poe.getMessage());
751
		} catch (AccessControlException ace) {
752
			logMetacat.error("ReplicationService.handleForceReplicateRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
753
			logReplication.error("ReplicationService.handleForceReplicateRequest - Permissions order error when adding doc " + docid + 
754
					" to DB with action " + dbaction + ": " + ace.getMessage());
755
		} catch (Exception e) {
756
			logMetacat.error("ReplicationService.handleForceReplicateRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
757
			logReplication.error("ReplicationService.handleForceReplicateRequest - General error when adding doc " + docid + 
758
					" to DB with action " + dbaction + ": " + e.getMessage());
759
		} finally {
760
			// Return the checked out DBConnection
761
			DBConnectionPool.returnDBConnection(dbConn, serialNumber);
762
		}//finally
763
	}
764

    
765
	/*
766
	 * when a forcereplication delete request comes in, local host will delete this
767
	 * document
768
	 */
769
	protected static void handleForceReplicateDeleteRequest(
770
			Hashtable<String, String[]> params, HttpServletResponse response,
771
			HttpServletRequest request, boolean removeAll) {
772
		String server = ((String[]) params.get("server"))[0]; // the server that
773
		String docid = ((String[]) params.get("docid"))[0]; // sent the document
774
		try {
775
			logReplication.info("ReplicationService.handleForceReplicateDeleteRequest - force replication delete request from " + server);
776
			logReplication.info("ReplicationService.handleForceReplicateDeleteRequest - force replication delete docid " + docid);
777
			logReplication.info("ReplicationService.handleForceReplicateDeleteRequest - Force replication delete request from: " + server);
778
			logReplication.info("ReplicationService.handleForceReplicateDeleteRequest - Force replication delete docid: " + docid);
779
			DocumentImpl.delete(docid, null, null, server, removeAll);
780
			logReplication.info("ReplicationService.handleForceReplicateDeleteRequest - document " + docid + " was successfully deleted ");
781
			EventLog.getInstance().log(request.getRemoteAddr(), request.getHeader("User-Agent"), REPLICATIONUSER, docid,
782
					"delete");
783
			logReplication.info("ReplicationService.handleForceReplicateDeleteRequest - document " + docid + " was successfully deleted ");
784
		} catch (McdbDocNotFoundException e) {
785
			logMetacat.error("ReplicationService.handleForceReplicateDeleteRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
786
			logReplication.error("document " + docid
787
					+ " failed to delete because " + e.getMessage());
788
			logReplication.error("ReplicationService.handleForceReplicateDeleteRequest - error: " + e.getMessage());
789
		} catch (InsufficientKarmaException e) {
790
			logMetacat.error("ReplicationService.handleForceReplicateDeleteRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
791
			logReplication.error("document " + docid
792
					+ " failed to delete because " + e.getMessage());
793
			logReplication.error("ReplicationService.handleForceReplicateDeleteRequest - error: " + e.getMessage());
794
		} catch (SQLException e) {
795
			logMetacat.error("ReplicationService.handleForceReplicateDeleteRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
796
			logReplication.error("document " + docid
797
					+ " failed to delete because " + e.getMessage());
798
			logReplication.error("ReplicationService.handleForceReplicateDeleteRequest - error: " + e.getMessage());
799
		} catch (Exception e) {
800
			logMetacat.error("ReplicationService.handleForceReplicateDeleteRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
801
			logReplication.error("document " + docid
802
					+ " failed to delete because " + e.getMessage());
803
			logReplication.error("ReplicationService.handleForceReplicateDeleteRequest - error: " + e.getMessage());
804

    
805
		}//catch
806

    
807
	}
808

    
809
	/**
810
	 * when a forcereplication data file request comes in, local host sends a
811
	 * readdata request to the requesting server (remote server) for the specified
812
	 * docid. Then store it in local database and file system
813
	 */
814
	protected static void handleForceReplicateDataFileRequest(Hashtable<String, String[]> params,
815
			HttpServletRequest request) {
816

    
817
		//make sure there is some parameters
818
		if (params.isEmpty()) {
819
			return;
820
		}
821
		// Get remote server
822
		String server = ((String[]) params.get("server"))[0];
823
		// the docid should include rev number
824
		String docid = ((String[]) params.get("docid"))[0];
825
		// Make sure there is a docid and server
826
		if (docid == null || server == null || server.equals("")) {
827
			logMetacat.error("ReplicationService.handleForceReplicateDataFileRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
828
			logReplication.error("ReplicationService.handleForceReplicateDataFileRequest - Didn't specify docid or server for replication");
829
			return;
830
		}
831

    
832
		// Overide or not
833
		//    boolean override = false;
834
		// dbaction - update or insert
835
		String dbaction = null;
836

    
837
		try {
838
			//docid was switch to two parts uinque code and rev
839
			//String uniqueCode=MetacatUtil.getDocIdFromString(docid);
840
			//int rev=MetacatUtil.getVersionFromString(docid);
841
			if (params.containsKey("dbaction")) {
842
				dbaction = ((String[]) params.get("dbaction"))[0];
843
			} else//default value is update
844
			{
845
//				dbaction = "update";
846
				dbaction = null;
847
			}
848

    
849
			logReplication.info("ReplicationService.handleForceReplicateDataFileRequest - Force replication request from: " + server);
850
			logReplication.info("ReplicationService.handleForceReplicateDataFileRequest - Force replication docid: " + docid);
851
			logReplication.info("ReplicationService.handleForceReplicateDataFileRequest - Force replication action: " + dbaction);
852
			// get the document info from server
853
			URL docinfourl = new URL("https://" + server + "?server="
854
					+ MetacatUtil.getLocalReplicationServerName()
855
					+ "&action=getdocumentinfo&docid=" + docid);
856

    
857
			String docInfoStr = ReplicationService.getURLContent(docinfourl);
858
			
859
			// strip out the system metadata portion
860
		    String systemMetadataXML = ReplicationUtil.getSystemMetadataContent(docInfoStr);
861
		   	docInfoStr = ReplicationUtil.getContentWithoutSystemMetadata(docInfoStr);
862

    
863
			//dih is the parser for the docinfo xml format
864
			DocInfoHandler dih = new DocInfoHandler();
865
			XMLReader docinfoParser = ReplicationHandler.initParser(dih);
866
			docinfoParser.parse(new InputSource(new StringReader(docInfoStr)));
867
			Hashtable<String, String> docinfoHash = dih.getDocInfo();
868
			
869
			String docName = (String) docinfoHash.get("docname");
870

    
871
			String docType = (String) docinfoHash.get("doctype");
872

    
873
			String docHomeServer = (String) docinfoHash.get("home_server");
874
			
875
			String createdDateString = docinfoHash.get("date_created");
876
			String updatedDateString = docinfoHash.get("date_updated");
877
			
878
			Date createdDate = DateTimeMarshaller.deserializeDateToUTC(createdDateString);
879
			Date updatedDate = DateTimeMarshaller.deserializeDateToUTC(updatedDateString);
880
			
881
			logReplication.info("ReplicationService.handleForceReplicateDataFileRequest - docHomeServer of datafile: " + docHomeServer);
882

    
883
			// in case we have a write exception, we still want to track access and sysmeta
884
			Exception writeException = null;
885

    
886
			// do we need the object content?
887
			if (dbaction != null && (dbaction.equals("insert") || dbaction.equals("update"))) {
888
				//Get data file and store it into local file system.
889
				// sending back readdata request to server
890
				URL url = new URL("https://" + server + "?server="
891
						+ MetacatUtil.getLocalReplicationServerName()
892
						+ "&action=readdata&docid=" + docid);
893
				String datafilePath = PropertyService
894
						.getProperty("application.datafilepath");
895

    
896
				InputStream inputStream = getURLStream(url);
897
				
898
				//register data file into xml_documents table and write data file
899
				//into file system
900
				try {
901
					DocumentImpl.writeDataFileInReplication(inputStream,
902
							datafilePath, docName, docType, docid, null, docHomeServer,
903
							server, DocumentImpl.DOCUMENTTABLE, false, createdDate,
904
							updatedDate);
905
				} catch (Exception e) {
906
					writeException = e;
907
				}
908

    
909
			}
910
			
911
			// process the real owner and updater
912
			DBConnection dbConn = DBConnectionPool.getDBConnection("ReplicationService.handleForceDataFileRequest");
913
	        int serialNumber = dbConn.getCheckOutSerialNumber();
914
	        dbConn.setAutoCommit(false);
915
			String user = (String) docinfoHash.get("user_owner");
916
			String updated = (String) docinfoHash.get("user_updated");
917
	        updateUserOwner(dbConn, docid, user, updated);
918
	        DBConnectionPool.returnDBConnection(dbConn, serialNumber);
919
	        
920
			// process system metadata
921
	        if (systemMetadataXML != null) {
922
	      	  SystemMetadata sysMeta = 
923
	      		TypeMarshaller.unmarshalTypeFromStream(
924
	      				  SystemMetadata.class, 
925
	      				  new ByteArrayInputStream(systemMetadataXML.getBytes("UTF-8")));
926
	      	  
927
	      	  // need the guid-to-docid mapping
928
	      	  boolean mappingExists = true;
929
	      	  mappingExists = IdentifierManager.getInstance().mappingExists(sysMeta.getIdentifier().getValue());
930
	      	  if (!mappingExists) {
931
	      		  IdentifierManager.getInstance().createMapping(sysMeta.getIdentifier().getValue(), docid);
932
	      	  }
933
	      	  // save the system metadata
934
	      	  HazelcastService.getInstance().getSystemMetadataMap().put(sysMeta.getIdentifier(), sysMeta);
935
	      	  // submit for indexing
936
              MetacatSolrIndex.getInstance().submit(sysMeta.getIdentifier(), sysMeta, null);
937
	        }
938
	        
939
	        // process the access control
940
	        try {
941
	        	// check if we had a guid -> docid mapping
942
	        	String docidNoRev = DocumentUtil.getDocIdFromAccessionNumber(docid);
943
	        	int rev = DocumentUtil.getRevisionFromAccessionNumber(docid);
944
	        	IdentifierManager.getInstance().getGUID(docidNoRev, rev);
945
	        	// no need to create the mapping if we have it
946
	        } catch (McdbDocNotFoundException mcdbe) {
947
	        	// create mapping if we don't
948
	        	IdentifierManager.getInstance().createMapping(docid, docid);
949
	        }
950
	        Vector<XMLAccessDAO> accessControlList = dih.getAccessControlList();
951
	        if (accessControlList != null) {
952
	        	AccessControlForSingleFile acfsf = new AccessControlForSingleFile(docid);
953
	        	for (XMLAccessDAO xmlAccessDAO : accessControlList) {
954
	        		if (!acfsf.accessControlExists(xmlAccessDAO)) {
955
	        			acfsf.insertPermissions(xmlAccessDAO);
956
						logReplication.info("ReplicationService.handleForceReplicateRequest - document " + docid
957
								+ " permissions added to DB");
958
	        		}
959
	            }
960
	        }
961
	        
962
	        // throw the write exception now -- this happens when access changes on an object
963
			if (writeException != null) {
964
				throw writeException;
965
			}
966

    
967
			logReplication.info("ReplicationService.handleForceReplicateDataFileRequest - datafile " + docid + " added to DB with "
968
					+ "action " + dbaction);
969
			EventLog.getInstance().log(request.getRemoteAddr(), request.getHeader("User-Agent"), REPLICATIONUSER,
970
					docid, dbaction);
971

    
972
		} catch (Exception e) {
973
			e.printStackTrace();
974
			logMetacat.error("ReplicationService.handleForceReplicateDataFileRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG, e);                         
975
			logReplication.error("ReplicationService.handleForceReplicateDataFileRequest - Datafile " + docid
976
					+ " failed to added to DB with " + "action " + dbaction + " because "
977
					+ e.getMessage());
978
			logReplication.error("ReplicationService.handleForceReplicateDataFileRequest - ERROR in MetacatReplication.handleForceDataFileReplicate"
979
					+ "Request(): " + e.getMessage());
980
		}
981
	}
982

    
983
	/**
984
	 * Grants or denies a lock to a requesting host.
985
	 * The servlet parameters of interrest are:
986
	 * docid: the docid of the file the lock is being requested for
987
	 * currentdate: the timestamp of the document on the remote server
988
	 *
989
	 */
990
	protected static void handleGetLockRequest(
991
			Hashtable<String, String[]> params, HttpServletResponse response) {
992

    
993
		try {
994

    
995
			String docid = ((String[]) params.get("docid"))[0];
996
			String remoteRev = ((String[]) params.get("updaterev"))[0];
997
			DocumentImpl requestDoc = new DocumentImpl(docid);
998
			logReplication.info("ReplicationService.handleGetLockRequest - lock request for " + docid);
999
			int localRevInt = requestDoc.getRev();
1000
			int remoteRevInt = Integer.parseInt(remoteRev);
1001

    
1002
			// get a writer for sending back to response
1003
			response.setContentType("text/xml");
1004
			Writer out = response.getWriter();
1005
			
1006
			if (remoteRevInt >= localRevInt) {
1007
				if (!fileLocks.contains(docid)) { //grant the lock if it is not already locked
1008
					fileLocks.add(0, docid); //insert at the beginning of the queue Vector
1009
					//send a message back to the the remote host authorizing the insert
1010
					out.write("<lockgranted><docid>" + docid
1011
									+ "</docid></lockgranted>");
1012
					//          lockThread = new Thread(this);
1013
					//          lockThread.setPriority(Thread.MIN_PRIORITY);
1014
					//          lockThread.start();
1015
					logReplication.info("ReplicationService.handleGetLockRequest - lock granted for " + docid);
1016
				} else { //deny the lock
1017
					out.write("<filelocked><docid>" + docid + "</docid></filelocked>");
1018
					logReplication.info("ReplicationService.handleGetLockRequest - lock denied for " + docid
1019
							+ "reason: file already locked");
1020
				}
1021
			} else {//deny the lock.
1022
				out.write("<outdatedfile><docid>" + docid + "</docid></filelocked>");
1023
				logReplication.info("ReplicationService.handleGetLockRequest - lock denied for " + docid
1024
						+ "reason: client has outdated file");
1025
			}
1026
			out.close();
1027
			//conn.close();
1028
		} catch (Exception e) {
1029
			logMetacat.error("ReplicationService.handleGetLockRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
1030
			logReplication.error("ReplicationService.handleGetLockRequest - error requesting file lock from MetacatReplication."
1031
					+ "handleGetLockRequest: " + e.getMessage());
1032
			e.printStackTrace(System.out);
1033
		}
1034
	}
1035

    
1036
	/**
1037
	 * Sends all of the xml_documents information encoded in xml to a requestor
1038
	 * the format is:
1039
	 * <!ELEMENT documentinfo (docid, docname, doctype, doctitle, user_owner,
1040
	 *                  user_updated, home_server, public_access, rev)/>
1041
	 * all of the subelements of document info are #PCDATA
1042
	 */
1043
	protected static void handleGetDocumentInfoRequest(
1044
			Hashtable<String, String[]> params, HttpServletResponse response) {
1045
		String docid = ((String[]) (params.get("docid")))[0];
1046

    
1047
		try {
1048
			// get docinfo as XML string
1049
			String docinfoXML = getDocumentInfo(docid);
1050
			
1051
			// get a writer for sending back to response
1052
			response.setContentType("text/xml");
1053
			Writer out = response.getWriter();
1054
			out.write(docinfoXML);
1055
			out.close();
1056

    
1057
		} catch (Exception e) {
1058
			logMetacat.error("ReplicationService.handleGetDocumentInfoRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
1059
			logReplication.error("ReplicationService.handleGetDocumentInfoRequest - error in metacatReplication.handlegetdocumentinforequest "
1060
					+ "for doc: " + docid + " : " + e.getMessage());
1061
		}
1062

    
1063
	}
1064
	
1065
	public static Hashtable<String, String> getDocumentInfoMap(String docid)
1066
			throws HandlerException, AccessControlException, JiBXException,
1067
			IOException, McdbException, SAXException {
1068
		
1069
		// Try get docid info from remote server
1070
		DocInfoHandler dih = new DocInfoHandler();
1071
		XMLReader docinfoParser = ReplicationHandler.initParser(dih);
1072

    
1073
		String docInfoStr = getDocumentInfo(docid);
1074

    
1075
		// strip out the system metadata portion
1076
		String systemMetadataXML = ReplicationUtil.getSystemMetadataContent(docInfoStr);
1077
		docInfoStr = ReplicationUtil.getContentWithoutSystemMetadata(docInfoStr);
1078

    
1079
		docinfoParser.parse(new InputSource(new StringReader(docInfoStr)));
1080
		Hashtable<String, String> docinfoHash = dih.getDocInfo();
1081

    
1082
		return docinfoHash;
1083
	}
1084
	
1085
	/**
1086
	 * Gets a docInfo XML snippet for the replication API
1087
	 * @param docid
1088
	 * @return
1089
	 * @throws AccessControlException
1090
	 * @throws JiBXException
1091
	 * @throws IOException
1092
	 * @throws McdbException
1093
	 */
1094
	public static String getDocumentInfo(String docid) throws AccessControlException, JiBXException, IOException, McdbException {
1095
		StringBuffer sb = new StringBuffer();
1096

    
1097
		DocumentImpl doc = new DocumentImpl(docid);
1098
		sb.append("<documentinfo><docid>").append(docid);
1099
		sb.append("</docid>");
1100
		
1101
		try {
1102
			// serialize the System Metadata as XML for docinfo
1103
			String guid = IdentifierManager.getInstance().getGUID(doc.getDocID(), doc.getRev());
1104
			SystemMetadata systemMetadata = IdentifierManager.getInstance().getSystemMetadata(guid);
1105
			ByteArrayOutputStream baos = new ByteArrayOutputStream();
1106
			TypeMarshaller.marshalTypeToOutputStream(systemMetadata, baos);
1107
			String systemMetadataXML = baos.toString("UTF-8");
1108
			sb.append("<systemMetadata>");
1109
			sb.append(systemMetadataXML);
1110
			sb.append("</systemMetadata>");
1111
		} catch(McdbDocNotFoundException e) {
1112
		  logMetacat.warn("No SystemMetadata found for: " + docid);
1113
		}
1114
		
1115
		Calendar created = Calendar.getInstance();
1116
		created.setTime(doc.getCreateDate());
1117
		Calendar updated = Calendar.getInstance();
1118
		updated.setTime(doc.getUpdateDate());
1119
		
1120
		sb.append("<docname><![CDATA[").append(doc.getDocname());
1121
		sb.append("]]></docname><doctype>").append(doc.getDoctype());
1122
		sb.append("</doctype>");
1123
		sb.append("<user_owner>").append(doc.getUserowner());
1124
		sb.append("</user_owner><user_updated>").append(doc.getUserupdated());
1125
		sb.append("</user_updated>");
1126
		sb.append("<date_created>");
1127
		sb.append(DateTimeMarshaller.serializeDateToUTC(doc.getCreateDate()));
1128
		sb.append("</date_created>");
1129
		sb.append("<date_updated>");
1130
		sb.append(DateTimeMarshaller.serializeDateToUTC(doc.getUpdateDate()));
1131
		sb.append("</date_updated>");
1132
		sb.append("<home_server>");
1133
		sb.append(doc.getDocHomeServer());
1134
		sb.append("</home_server>");
1135
		sb.append("<public_access>").append(doc.getPublicaccess());
1136
		sb.append("</public_access><rev>").append(doc.getRev());
1137
		sb.append("</rev>");
1138

    
1139
		sb.append("<accessControl>");
1140

    
1141
		AccessControlForSingleFile acfsf = new AccessControlForSingleFile(docid); 
1142
		sb.append(acfsf.getAccessString());
1143
		
1144
		sb.append("</accessControl>");
1145

    
1146
		sb.append("</documentinfo>");
1147
			
1148
		return sb.toString();
1149
	}
1150
	
1151
	/**
1152
	 * Sends System Metadata as XML
1153
	 */
1154
	protected static void handleGetSystemMetadataRequest(
1155
			Hashtable<String, String[]> params, HttpServletResponse response) {
1156
		String guid = ((String[]) (params.get("guid")))[0];
1157
		String systemMetadataXML = null;
1158
		try {
1159
			
1160
			// serialize the System Metadata as XML 
1161
			SystemMetadata systemMetadata = IdentifierManager.getInstance().getSystemMetadata(guid);
1162
			ByteArrayOutputStream baos = new ByteArrayOutputStream();
1163
			TypeMarshaller.marshalTypeToOutputStream(systemMetadata, baos);
1164
			systemMetadataXML = baos.toString("UTF-8");
1165
				
1166
			// get a writer for sending back to response
1167
			response.setContentType("text/xml");
1168
			Writer out = response.getWriter();
1169
			out.write(systemMetadataXML);
1170
			out.close();
1171

    
1172
		} catch (Exception e) {
1173
			String msg = "ReplicationService.handleGetSystemMetadataRequest for guid: " + guid + " : " + e.getMessage();
1174
			logMetacat.error(msg);                         
1175
			logReplication.error(msg);
1176
		}
1177

    
1178
	}
1179
	
1180
	/**
1181
	 * when a forcereplication request comes in, local host sends a read request
1182
	 * to the requesting server (remote server) for the specified docid. Then
1183
	 * store it in local database.
1184
	 */
1185
	protected static void handleForceReplicateSystemMetadataRequest(
1186
			Hashtable<String, String[]> params, HttpServletResponse response,
1187
			HttpServletRequest request) {
1188
		String server = ((String[]) params.get("server"))[0]; // the server that
1189
		String guid = ((String[]) params.get("guid"))[0]; // sent the document
1190
		
1191
		try {
1192
			logReplication.info("ReplicationService.handleForceReplicateSystemMetadataRequest - Force replication system metadata request from: " + server);
1193
			// get the system metadata from server
1194
			URL docinfourl = new URL("https://" + server + "?server="
1195
					+ MetacatUtil.getLocalReplicationServerName()
1196
					+ "&action=getsystemmetadata&guid=" + guid);
1197
			
1198
			String systemMetadataXML = ReplicationService.getURLContent(docinfourl);
1199
						
1200
			// process system metadata
1201
			if (systemMetadataXML != null) {
1202
				SystemMetadata sysMeta = 
1203
					TypeMarshaller.unmarshalTypeFromStream(
1204
							SystemMetadata.class,
1205
							new ByteArrayInputStream(systemMetadataXML.getBytes("UTF-8")));
1206
				HazelcastService.getInstance().getSystemMetadataMap().put(sysMeta.getIdentifier(), sysMeta);
1207
				// submit for indexing
1208
                MetacatSolrIndex.getInstance().submit(sysMeta.getIdentifier(), sysMeta, null);
1209
			}
1210
      
1211
			logReplication.info("ReplicationService.handleForceReplicateSystemMetadataRequest - processed guid: " + guid);
1212
			EventLog.getInstance().log(request.getRemoteAddr(), request.getHeader("User-Agent"), REPLICATIONUSER, guid, "systemMetadata");
1213

    
1214
		} catch (Exception e) {
1215
			logMetacat.error("ReplicationService.handleForceReplicateRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG, e);                         
1216
			logReplication.error("ReplicationService.handleForceReplicateRequest - General error when processing guid: " + guid, e);
1217
		}
1218
	}
1219

    
1220
	/**
1221
	 * Sends a datafile to a remote host
1222
	 */
1223
	protected static void handleGetDataFileRequest(OutputStream outPut,
1224
			Hashtable<String, String[]> params, HttpServletResponse response)
1225

    
1226
	{
1227
		// File path for data file
1228
		String filepath;
1229
		// Request docid
1230
		String docId = ((String[]) (params.get("docid")))[0];
1231
		//check if the doicd is null
1232
		if (docId == null) {
1233
			logMetacat.error("ReplicationService.handleGetDataFileRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
1234
			logReplication.error("ReplicationService.handleGetDataFileRequest - Didn't specify docid for replication");
1235
			return;
1236
		}
1237

    
1238
		//try to open a https stream to test if the request server's public key
1239
		//in the key store, this is security issue
1240
		try {
1241
			filepath = PropertyService.getProperty("application.datafilepath");
1242
			String server = params.get("server")[0];
1243
			URL u = new URL("https://" + server + "?server="
1244
					+ MetacatUtil.getLocalReplicationServerName() + "&action=test");
1245
			String test = ReplicationService.getURLContent(u);
1246
			//couldn't pass the test
1247
			if (test.indexOf("successfully") == -1) {
1248
				//response.setContentType("text/xml");
1249
				//outPut.println("<error>Couldn't pass the trust test</error>");
1250
				logMetacat.error("ReplicationService.handleGetDataFileRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
1251
				logReplication.error("ReplicationService.handleGetDataFileRequest - Couldn't pass the trust test");
1252
				return;
1253
			}
1254
		}//try
1255
		catch (Exception ee) {
1256
			return;
1257
		}//catch
1258

    
1259
		if (!filepath.endsWith("/")) {
1260
			filepath += "/";
1261
		}
1262
		// Get file aboslute file name
1263
		String filename = filepath + docId;
1264

    
1265
		//MIME type
1266
		String contentType = null;
1267
		if (filename.endsWith(".xml")) {
1268
			contentType = "text/xml";
1269
		} else if (filename.endsWith(".css")) {
1270
			contentType = "text/css";
1271
		} else if (filename.endsWith(".dtd")) {
1272
			contentType = "text/plain";
1273
		} else if (filename.endsWith(".xsd")) {
1274
			contentType = "text/xml";
1275
		} else if (filename.endsWith("/")) {
1276
			contentType = "text/html";
1277
		} else {
1278
			File f = new File(filename);
1279
			if (f.isDirectory()) {
1280
				contentType = "text/html";
1281
			} else {
1282
				contentType = "application/octet-stream";
1283
			}
1284
		}
1285

    
1286
		// Set the mime type
1287
		response.setContentType(contentType);
1288

    
1289
		// Get the content of the file
1290
		FileInputStream fin = null;
1291
		try {
1292
			// FileInputStream to metacat
1293
			fin = new FileInputStream(filename);
1294
			// 4K buffer
1295
			byte[] buf = new byte[4 * 1024];
1296
			// Read data from file input stream to byte array
1297
			int b = fin.read(buf);
1298
			// Write to outStream from byte array
1299
			while (b != -1) {
1300
				outPut.write(buf, 0, b);
1301
				b = fin.read(buf);
1302
			}
1303
			// close file input stream
1304
			fin.close();
1305

    
1306
		} catch (Exception e) {
1307
			logMetacat.error("ReplicationService.handleGetDataFileRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
1308
			logReplication.error("ReplicationService.handleGetDataFileRequest - error getting data file from MetacatReplication."
1309
					+ "handlGetDataFileRequest " + e.getMessage());
1310
			e.printStackTrace(System.out);
1311
		} finally {
1312
		    IOUtils.closeQuietly(fin);
1313
		}
1314

    
1315
	}
1316

    
1317
	/**
1318
	 * Sends a document to a remote host
1319
	 */
1320
	protected static void handleGetDocumentRequest(
1321
			Hashtable<String, String[]> params, HttpServletResponse response) {
1322

    
1323
		String urlString = null;
1324
		String documentPath = null;
1325
		String errorMsg = null;
1326
		FileOutputStream fos = null;
1327
		InputStream is = null;
1328
		OutputStream outputStream = null;
1329
		try {
1330
			// try to open a https stream to test if the request server's public
1331
			// key
1332
			// in the key store, this is security issue
1333
			String server = params.get("server")[0];
1334
			urlString = "https://" + server + "?server="
1335
					+ MetacatUtil.getLocalReplicationServerName() + "&action=test";
1336
			URL u = new URL(urlString);
1337
			String test = ReplicationService.getURLContent(u);
1338
			// couldn't pass the test
1339
			if (test.indexOf("successfully") == -1) {
1340
				response.setContentType("text/xml");
1341
				Writer out = response.getWriter();
1342
				out.write("<error>Couldn't pass the trust test " + test + " </error>");
1343
				out.close();
1344
				return;
1345
			}
1346

    
1347
			String docid = params.get("docid")[0];
1348
			logReplication.debug("ReplicationService.handleGetDocumentRequest - MetacatReplication.handleGetDocumentRequest for docid: "
1349
					+ docid);
1350
			DocumentImpl di = new DocumentImpl(docid);
1351

    
1352
			String documentDir = PropertyService
1353
					.getProperty("application.documentfilepath");
1354
			documentPath = documentDir + FileUtil.getFS() + docid;
1355

    
1356
			// if the document does not exist on disk, read it from db and write
1357
			// it to disk.
1358
			if (FileUtil.getFileStatus(documentPath) == FileUtil.DOES_NOT_EXIST
1359
					|| FileUtil.getFileSize(documentPath) == 0) {
1360
				fos = new FileOutputStream(documentPath);
1361
				is = di.toXml(fos, null, null, true);
1362
				fos.close();
1363
				is.close();
1364
			}
1365

    
1366
			// read the file from disk and send it to outputstream
1367
			outputStream = response.getOutputStream();
1368
			is = di.readFromFileSystem(outputStream, null, null, documentPath);
1369
			is.close();
1370
			outputStream.close();
1371

    
1372
			logReplication.info("ReplicationService.handleGetDocumentRequest - document " + docid + " sent");
1373

    
1374
			// return to avoid continuing to the error reporting section at the end
1375
			return;
1376
			
1377
		} catch (MalformedURLException mue) {
1378
			logMetacat.error("ReplicationService.handleGetDocumentRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
1379
			logReplication.error("ReplicationService.handleGetDocumentRequest - Url error when getting document from MetacatReplication."
1380
					+ "handlGetDocumentRequest for url: " + urlString + " : "
1381
					+ mue.getMessage());
1382
			// e.printStackTrace(System.out);
1383
			
1384
		} catch (IOException ioe) {
1385
			logMetacat.error("ReplicationService.handleGetDocumentRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
1386
			logReplication.error("ReplicationService.handleGetDocumentRequest - I/O error when getting document from MetacatReplication."
1387
					+ "handlGetDocumentRequest for file: " + documentPath + " : "
1388
					+ ioe.getMessage());
1389
			errorMsg = ioe.getMessage();
1390
		} catch (PropertyNotFoundException pnfe) {
1391
			logMetacat.error("ReplicationService.handleGetDocumentRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
1392
			logReplication
1393
					.error("ReplicationService.handleGetDocumentRequest - Error getting property when getting document from MetacatReplication."
1394
							+ "handlGetDocumentRequest for file: "
1395
							+ documentPath
1396
							+ " : "
1397
							+ pnfe.getMessage());
1398
			// e.printStackTrace(System.out);
1399
			errorMsg = pnfe.getMessage();
1400
		} catch (McdbException me) {
1401
			logReplication
1402
					.error("ReplicationService.handleGetDocumentRequest - Document implementation error  getting property when getting document from MetacatReplication."
1403
							+ "handlGetDocumentRequest for file: "
1404
							+ documentPath
1405
							+ " : "
1406
							+ me.getMessage());
1407
			// e.printStackTrace(System.out);
1408
			errorMsg = me.getMessage();
1409
		} finally {
1410
            IOUtils.closeQuietly(fos);
1411
            IOUtils.closeQuietly(is);
1412
            IOUtils.closeQuietly(outputStream);
1413
		}
1414
		
1415
		// report any errors if we got here
1416
		response.setContentType("text/xml");
1417
		Writer out = null;
1418
		try {
1419
			response.getWriter();
1420
			out = response.getWriter();
1421
			out.write("<error>" + errorMsg + "</error>");
1422
		} catch (Exception e) {
1423
			logMetacat.error(e.getMessage(), e);
1424
		} finally {
1425
			try {
1426
				out.close();
1427
			} catch (IOException e) {
1428
				logMetacat.error(e.getMessage(), e);
1429
			}
1430
		}
1431
		
1432

    
1433
	}
1434

    
1435
	/**
1436
	 * Sends a list of all of the documents on this sever along with their
1437
	 * revision numbers. The format is: <!ELEMENT replication (server, updates)>
1438
	 * <!ELEMENT server (#PCDATA)> <!ELEMENT updates ((updatedDocument |
1439
	 * deleteDocument | revisionDocument)*)> <!ELEMENT updatedDocument (docid,
1440
	 * rev, datafile*)> <!ELEMENT deletedDocument (docid, rev)> <!ELEMENT
1441
	 * revisionDocument (docid, rev, datafile*)> <!ELEMENT docid (#PCDATA)>
1442
	 * <!ELEMENT rev (#PCDATA)> <!ELEMENT datafile (#PCDATA)> note that the rev
1443
	 * in deletedDocument is always empty. I just left it in there to make the
1444
	 * parser implementation easier.
1445
	 */
1446
	protected static void handleUpdateRequest(Hashtable<String, String[]> params,
1447
			HttpServletResponse response) {
1448
		// Checked out DBConnection
1449
		DBConnection dbConn = null;
1450
		// DBConenction serial number when checked it out
1451
		int serialNumber = -1;
1452
		PreparedStatement pstmt = null;
1453
		// Server list to store server info of xml_replication table
1454
		ReplicationServerList serverList = null;
1455
		
1456
		// a writer for response
1457
		Writer out = null;
1458

    
1459
		try {
1460
			// get writer, TODO: encoding?
1461
			response.setContentType("text/xml");
1462
			out = response.getWriter();
1463
			
1464
			// Check out a DBConnection from pool
1465
			dbConn = DBConnectionPool
1466
					.getDBConnection("MetacatReplication.handleUpdateRequest");
1467
			serialNumber = dbConn.getCheckOutSerialNumber();
1468
			// Create a server list from xml_replication table
1469
			serverList = new ReplicationServerList();
1470

    
1471
			// Get remote server name from param
1472
			String server = ((String[]) params.get("server"))[0];
1473
			// If no servr name in param, return a error
1474
			if (server == null || server.equals("")) {
1475
				out.write("<error>Request didn't specify server name</error>");
1476
				out.close();
1477
				return;
1478
			}//if
1479

    
1480
			//try to open a https stream to test if the request server's public key
1481
			//in the key store, this is security issue
1482
			String testUrl = "https://" + server + "?server="
1483
            + MetacatUtil.getLocalReplicationServerName() + "&action=test";
1484
			logReplication.info("Running trust test: " + testUrl);
1485
			URL u = new URL(testUrl);
1486
			String test = ReplicationService.getURLContent(u);
1487
			logReplication.info("Ouput from test is '" + test + "'");
1488
			//couldn't pass the test
1489
			if (test.indexOf("successfully") == -1) {
1490
			    logReplication.error("Trust test failed.");
1491
				out.write("<error>Couldn't pass the trust test</error>");
1492
				out.close();
1493
				return;
1494
			}
1495
			logReplication.info("Trust test succeeded.");
1496

    
1497
			// Check if local host configure to replicate xml documents to remote
1498
			// server. If not send back a error message
1499
			if (!serverList.getReplicationValue(server)) {
1500
				out.write("<error>Configuration not allow to replicate document to you</error>");
1501
				out.close();
1502
				return;
1503
			}//if
1504

    
1505
			// Store the sql command
1506
			StringBuffer docsql = new StringBuffer();
1507
			StringBuffer revisionSql = new StringBuffer();
1508
			
1509
			// Store the data set file
1510
			Vector<Vector<String>> packageFiles = new Vector<Vector<String>>();
1511

    
1512
			// Append local server's name and replication servlet to doclist
1513
			out.append("<?xml version=\"1.0\"?><replication>");
1514
			out.append("<server>")
1515
					.append(MetacatUtil.getLocalReplicationServerName());
1516
			//doclist.append(util.getProperty("replicationpath"));
1517
			out.append("</server><updates>");
1518

    
1519
			// Get correct docid that reside on this server according the requesting
1520
			// server's replicate and data replicate value in xml_replication table
1521
			docsql.append(DatabaseService.getInstance().getDBAdapter().getReplicationDocumentListSQL());
1522
			//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)) ");
1523
			revisionSql.append("select docid, rev, doctype from xml_revisions ");
1524
			// If the localhost is not a hub to the remote server, only replicate
1525
			// the docid' which home server is local host (server_location =1)
1526
			if (!serverList.getHubValue(server)) {
1527
				String serverLocationDoc = " and a.server_location = 1";
1528
				String serverLocationRev = "where server_location = 1";
1529
				docsql.append(serverLocationDoc);
1530
				revisionSql.append(serverLocationRev);
1531
			}
1532
			logReplication.info("ReplicationService.handleUpdateRequest - Doc sql: " + docsql.toString());
1533

    
1534
			// Get any deleted documents
1535
			StringBuffer delsql = new StringBuffer();
1536
			delsql.append("SELECT t1.docid FROM xml_revisions t1 LEFT JOIN xml_documents t2 on t1.docid = t2.docid WHERE t2.docid IS NULL "); 
1537
			
1538
			// If the localhost is not a hub to the remote server, only replicate
1539
			// the docid' which home server is local host (server_location =1)
1540
			if (!serverList.getHubValue(server)) {
1541
				delsql.append("and t1.server_location = 1");
1542
			}
1543
			logReplication.info("ReplicationService.handleUpdateRequest - Deleted sql: " + delsql.toString());
1544

    
1545
			// Get docid list of local host
1546
			pstmt = dbConn.prepareStatement(docsql.toString());
1547
			pstmt.execute();
1548
			ResultSet rs = pstmt.getResultSet();
1549
			boolean tablehasrows = rs.next();
1550
			//If metacat configed to replicate data file
1551
			//if ((util.getProperty("replicationsenddata")).equals("on"))
1552
			boolean replicateData = serverList.getDataReplicationValue(server);
1553
			if (replicateData) {
1554
				while (tablehasrows) {
1555
					String recordDoctype = rs.getString(3);
1556
					Vector<String> packagedoctypes = MetacatUtil
1557
							.getOptionList(PropertyService
1558
									.getProperty("xml.packagedoctype"));
1559
					//if this is a package file, put it at the end
1560
					//because if a package file is read before all of the files it
1561
					//refers to are loaded then there is an error
1562
					if (recordDoctype != null && !packagedoctypes.contains(recordDoctype)) {
1563
						//If this is not data file
1564
						if (!recordDoctype.equals("BIN")) {
1565
							//for non-data file document
1566
							out.append("<updatedDocument>");
1567
							out.append("<docid>").append(rs.getString(1));
1568
							out.append("</docid><rev>" + rs.getInt(2));
1569
							out.append("</rev>");
1570
							out.append("</updatedDocument>");
1571
						}//if
1572
						else {
1573
							//for data file document, in datafile attributes
1574
							//we put "datafile" value there
1575
							out.append("<updatedDocument>");
1576
							out.append("<docid>").append(rs.getString(1));
1577
							out.append("</docid><rev>" + rs.getInt(2));
1578
							out.append("</rev>");
1579
							out.append("<datafile>");
1580
							out.append(DATA_FILE_FLAG);
1581
							out.append("</datafile>");
1582
							out.append("</updatedDocument>");
1583
						}//else
1584
					}//if packagedoctpes
1585
					else { //the package files are saved to be put into the xml later.
1586
						Vector<String> v = new Vector<String>();
1587
						v.add(rs.getString(1));
1588
						v.add(String.valueOf(rs.getInt(2)));
1589
						packageFiles.add(v);
1590
					}//esle
1591
					tablehasrows = rs.next();
1592
				}//while
1593
			}//if
1594
			else //metacat was configured not to send data file
1595
			{
1596
				while (tablehasrows) {
1597
					String recordDoctype = rs.getString(3);
1598
					if (!recordDoctype.equals("BIN")) { //don't replicate data files
1599
						Vector<String> packagedoctypes = MetacatUtil
1600
								.getOptionList(PropertyService
1601
										.getProperty("xml.packagedoctype"));
1602
						if (recordDoctype != null
1603
								&& !packagedoctypes.contains(recordDoctype)) { //if this is a package file, put it at the end
1604
							//because if a package file is read before all of the files it
1605
							//refers to are loaded then there is an error
1606
							out.append("<updatedDocument>");
1607
							out.append("<docid>" + rs.getString(1));
1608
							out.append("</docid><rev>" + rs.getInt(2));
1609
							out.append("</rev>");
1610
							out.append("</updatedDocument>");
1611
						} else { //the package files are saved to be put into the xml later.
1612
							Vector<String> v = new Vector<String>();
1613
							v.add(rs.getString(1));
1614
							v.add(String.valueOf(rs.getInt(2)));
1615
							packageFiles.add(v);
1616
						}
1617
					}//if
1618
					tablehasrows = rs.next();
1619
				}//while
1620
			}//else
1621

    
1622
			pstmt = dbConn.prepareStatement(delsql.toString());
1623
			//usage count should increas 1
1624
			dbConn.increaseUsageCount(1);
1625

    
1626
			pstmt.execute();
1627
			rs = pstmt.getResultSet();
1628
			tablehasrows = rs.next();
1629
			while (tablehasrows) { //handle the deleted documents
1630
				out.append("<deletedDocument><docid>").append(rs.getString(1));
1631
				out.append("</docid><rev></rev></deletedDocument>");
1632
				//note that rev is always empty for deleted docs
1633
				tablehasrows = rs.next();
1634
			}
1635

    
1636
			//now we can put the package files into the xml results
1637
			for (int i = 0; i < packageFiles.size(); i++) {
1638
				Vector<String> v = packageFiles.elementAt(i);
1639
				out.append("<updatedDocument>");
1640
				out.append("<docid>").append(v.elementAt(0));
1641
				out.append("</docid><rev>");
1642
				out.append(v.elementAt(1));
1643
				out.append("</rev>");
1644
				out.append("</updatedDocument>");
1645
			}
1646
			// add revision doc list  
1647
			out.append(prepareRevisionDoc(dbConn, revisionSql.toString(),
1648
					replicateData));
1649

    
1650
			out.append("</updates></replication>");
1651
			logReplication.info("ReplicationService.handleUpdateRequest - done writing to output stream.");
1652
			pstmt.close();
1653
			//conn.close();
1654

    
1655
		} catch (Exception e) {
1656
			logMetacat.error("ReplicationService.handleUpdateRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
1657
			logReplication.error("ReplicationService.handleUpdateRequest - error in MetacatReplication." + "handleupdaterequest: "
1658
					+ e.getMessage());
1659
			//e.printStackTrace(System.out);
1660
			try {
1661
				out.write("<error>" + e.getMessage() + "</error>");
1662
			} catch (IOException e1) {
1663
				logMetacat.error(e1.getMessage(), e1);
1664
			}
1665
		} finally {
1666
			try {
1667
				pstmt.close();
1668
			}//try
1669
			catch (SQLException ee) {
1670
				logMetacat.error("ReplicationService.handleUpdateRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
1671
				logReplication.error("ReplicationService.handleUpdateRequest - Error in MetacatReplication."
1672
						+ "handleUpdaterequest to close pstmt: " + ee.getMessage());
1673
			}//catch
1674
			finally {
1675
				DBConnectionPool.returnDBConnection(dbConn, serialNumber);
1676
			}//finally
1677
			try {
1678
				out.close();
1679
			} catch (IOException e) {
1680
				logMetacat.error(e.getMessage(), e);
1681
			}
1682
		}//finally
1683

    
1684
	}//handlUpdateRequest
1685

    
1686
	/**
1687
	 * 
1688
	 * @param dbConn connection for doing the update
1689
	 * @param docid the document id to update
1690
	 * @param owner the user_owner
1691
	 * @param updater the user_updated
1692
	 * @throws SQLException
1693
	 */
1694
	public static void updateUserOwner(DBConnection dbConn, String docid, String owner, String updater) throws SQLException {
1695
	
1696
		String sql = 
1697
			"UPDATE xml_documents " +
1698
			"SET user_owner = ?, " +
1699
			"user_updated = ? " +
1700
			"WHERE docid = ?;";
1701
		PreparedStatement pstmt = dbConn.prepareStatement(sql);
1702
		//usage count should increas 1
1703
		dbConn.increaseUsageCount(1);
1704

    
1705
		docid = DocumentUtil.getSmartDocId(docid);
1706
		pstmt.setString(1, owner);
1707
		pstmt.setString(2, updater);
1708
		pstmt.setString(3, docid);
1709
		pstmt.execute();
1710
		pstmt.close();
1711
		
1712
		dbConn.commit();
1713
	}
1714
	
1715
	/*
1716
	 * This method will get the xml string for document in xml_revision
1717
	 * The schema look like <!ELEMENT revisionDocument (docid, rev, datafile*)>
1718
	 */
1719
	private static String prepareRevisionDoc(DBConnection dbConn, String revSql,
1720
			boolean replicateData) throws Exception {
1721
		logReplication.warn("ReplicationService.prepareRevisionDoc - The revision document sql is " + revSql);
1722
		StringBuffer revDocList = new StringBuffer();
1723
		PreparedStatement pstmt = dbConn.prepareStatement(revSql);
1724
		//usage count should increas 1
1725
		dbConn.increaseUsageCount(1);
1726

    
1727
		pstmt.execute();
1728
		ResultSet rs = pstmt.getResultSet();
1729
		logReplication.warn("Processing replication revision for documents");
1730
		while (rs.next()) {
1731
			String recordDoctype = rs.getString(3);
1732

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

    
1737
				// do nothing
1738
			} else {
1739
				String docid = rs.getString(1);
1740
				int rev = rs.getInt(2);
1741
				logMetacat.debug("Processing replication revision for docid: " + docid + "." + rev);
1742

    
1743
				revDocList.append("<revisionDocument>");
1744
				revDocList.append("<docid>").append(docid);
1745
				revDocList.append("</docid><rev>").append(rev);
1746
				revDocList.append("</rev>");
1747
				// data file
1748
				if (recordDoctype.equals("BIN")) {
1749
					revDocList.append("<datafile>");
1750
					revDocList.append(DATA_FILE_FLAG);
1751
					revDocList.append("</datafile>");
1752
				}
1753
				revDocList.append("</revisionDocument>");
1754

    
1755
			}//else
1756
		}
1757
		//System.out.println("The revision list is"+ revDocList.toString());
1758
		return revDocList.toString();
1759
	}
1760

    
1761
	/**
1762
	 * Returns the xml_catalog table encoded in xml
1763
	 */
1764
	public static String getCatalogXML() {
1765
		return handleGetCatalogRequest(null, null, false);
1766
	}
1767

    
1768
	/**
1769
	 * Sends the contents of the xml_catalog table encoded in xml
1770
	 * The xml format is:
1771
	 * <!ELEMENT xml_catalog (row*)>
1772
	 * <!ELEMENT row (entry_type, source_doctype, target_doctype, public_id,
1773
	 *                system_id)>
1774
	 * All of the sub elements of row are #PCDATA
1775

    
1776
	 * If printFlag == false then do not print to out.
1777
	 */
1778
	protected static String handleGetCatalogRequest(
1779
			Hashtable<String, String[]> params, HttpServletResponse response,
1780
			boolean printFlag) {
1781
		DBConnection dbConn = null;
1782
		int serialNumber = -1;
1783
		PreparedStatement pstmt = null;
1784
		Writer out = null;
1785
		try {
1786
			// get writer, TODO: encoding?
1787
		    if(printFlag)
1788
		    {
1789
		        response.setContentType("text/xml");
1790
		        out = response.getWriter();
1791
		    }
1792
			/*conn = MetacatReplication.getDBConnection("MetacatReplication." +
1793
			                                          "handleGetCatalogRequest");*/
1794
			dbConn = DBConnectionPool
1795
					.getDBConnection("MetacatReplication.handleGetCatalogRequest");
1796
			serialNumber = dbConn.getCheckOutSerialNumber();
1797
			pstmt = dbConn.prepareStatement("select entry_type, "
1798
					+ "source_doctype, target_doctype, public_id, "
1799
					+ "system_id from xml_catalog");
1800
			pstmt.execute();
1801
			ResultSet rs = pstmt.getResultSet();
1802
			boolean tablehasrows = rs.next();
1803
			StringBuffer sb = new StringBuffer();
1804
			sb.append("<?xml version=\"1.0\"?><xml_catalog>");
1805
			while (tablehasrows) {
1806
				sb.append("<row><entry_type>").append(rs.getString(1));
1807
				sb.append("</entry_type><source_doctype>").append(rs.getString(2));
1808
				sb.append("</source_doctype><target_doctype>").append(rs.getString(3));
1809
				sb.append("</target_doctype><public_id>").append(rs.getString(4));
1810
				// system id may not have server url on front.  Add it if not.
1811
				String systemID = rs.getString(5);
1812
				if (!systemID.startsWith("http://")) {
1813
					systemID = SystemUtil.getContextURL() + systemID;
1814
				}
1815
				sb.append("</public_id><system_id>").append(systemID);
1816
				sb.append("</system_id></row>");
1817

    
1818
				tablehasrows = rs.next();
1819
			}
1820
			sb.append("</xml_catalog>");
1821
			//conn.close();
1822
			if (printFlag) {
1823
				response.setContentType("text/xml");
1824
				out.write(sb.toString());
1825
			}
1826
			pstmt.close();
1827
			return sb.toString();
1828
		} catch (Exception e) {
1829
			logMetacat.error("ReplicationService.handleGetCatalogRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
1830
			logReplication.error("ReplicationService.handleGetCatalogRequest - error in MetacatReplication.handleGetCatalogRequest:"
1831
					+ e.getMessage());
1832
			e.printStackTrace(System.out);
1833
			if (printFlag) {
1834
				try {
1835
					out.write("<error>" + e.getMessage() + "</error>");
1836
				} catch (IOException e1) {
1837
					logMetacat.error(e1.getMessage(), e1);
1838
				}
1839
			}
1840
		} finally {
1841
			try {
1842
				pstmt.close();
1843
			}//try
1844
			catch (SQLException ee) {
1845
				logMetacat.error("ReplicationService.handleGetCatalogRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
1846
				logReplication.error("ReplicationService.handleGetCatalogRequest - Error in MetacatReplication.handleGetCatalogRequest: "
1847
						+ ee.getMessage());
1848
			}//catch
1849
			finally {
1850
				DBConnectionPool.returnDBConnection(dbConn, serialNumber);
1851
			}//finally
1852
			if (out != null) {
1853
				try {
1854
					out.close();
1855
				} catch (IOException e1) {
1856
					logMetacat.error(e1.getMessage(), e1);
1857
				}
1858
			}
1859
		}//finally
1860

    
1861
		return null;
1862
	}
1863

    
1864
	/**
1865
	 * Sends the current system date to the remote server.  Using this action
1866
	 * for replication gets rid of any problems with syncronizing clocks
1867
	 * because a time specific to a document is always kept on its home server.
1868
	 */
1869
	protected static void handleGetTimeRequest(
1870
			Hashtable<String, String[]> params, HttpServletResponse response) {
1871
		
1872
		// use standard format -- the receiving end wants this too
1873
		String dateString = DateTimeMarshaller.serializeDateToUTC(Calendar.getInstance().getTime());
1874
		
1875
		// get a writer for sending back to response
1876
		response.setContentType("text/xml");
1877
		Writer out = null;
1878
		try {
1879
			out = response.getWriter();
1880
			out.write("<timestamp>" + dateString + "</timestamp>");
1881
			out.close();
1882
		} catch (IOException e) {
1883
			logMetacat.error(e.getMessage(), e);
1884
		}
1885
		
1886
	}
1887

    
1888
	/**
1889
	 * this method handles the timeout for a file lock.  when a lock is
1890
	 * granted it is granted for 30 seconds.  When this thread runs out
1891
	 * it deletes the docid from the queue, thus eliminating the lock.
1892
	 */
1893
	public void run() {
1894
		try {
1895
			logReplication.info("ReplicationService.run - thread started for docid: "
1896
					+ (String) fileLocks.elementAt(0));
1897

    
1898
			Thread.sleep(30000); //the lock will expire in 30 seconds
1899
			logReplication.info("thread for docid: "
1900
					+ (String) fileLocks.elementAt(fileLocks.size() - 1) + " exiting.");
1901

    
1902
			fileLocks.remove(fileLocks.size() - 1);
1903
			//fileLocks is treated as a FIFO queue.  If there are more than one lock
1904
			//in the vector, the first one inserted will be removed.
1905
		} catch (Exception e) {
1906
			logMetacat.error("ReplicationService.run - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
1907
			logReplication.error("ReplicationService.run - error in file lock thread from "
1908
					+ "MetacatReplication.run: " + e.getMessage());
1909
		}
1910
	}
1911

    
1912
	/**
1913
	 * Returns the name of a server given a serverCode
1914
	 * @param serverCode the serverid of the server
1915
	 * @return the servername or null if the specified serverCode does not
1916
	 *         exist.
1917
	 */
1918
	public static String getServerNameForServerCode(int serverCode) {
1919
		//System.out.println("serverid: " + serverCode);
1920
		DBConnection dbConn = null;
1921
		int serialNumber = -1;
1922
		PreparedStatement pstmt = null;
1923
		try {
1924
			dbConn = DBConnectionPool.getDBConnection("MetacatReplication.getServer");
1925
			serialNumber = dbConn.getCheckOutSerialNumber();
1926
			String sql = new String("select server from "
1927
					+ "xml_replication where serverid = ?");
1928
			pstmt = dbConn.prepareStatement(sql);
1929
			pstmt.setInt(1, serverCode);
1930
			//System.out.println("getserver sql: " + sql);
1931
			pstmt.execute();
1932
			ResultSet rs = pstmt.getResultSet();
1933
			boolean tablehasrows = rs.next();
1934
			if (tablehasrows) {
1935
				//System.out.println("server: " + rs.getString(1));
1936
				return rs.getString(1);
1937
			}
1938

    
1939
			//conn.close();
1940
		} catch (Exception e) {
1941
			logMetacat.error("ReplicationService.getServerNameForServerCode - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
1942
			logReplication.error("ReplicationService.getServerNameForServerCode - Error in MetacatReplication.getServer: " + e.getMessage());
1943
		} finally {
1944
			try {
1945
				pstmt.close();
1946
			}//try
1947
			catch (SQLException ee) {
1948
				logMetacat.error("ReplicationService.getServerNameForServerCode - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
1949
				logReplication.error("ReplicationService.getServerNameForServerCode - Error in MetacactReplication.getserver: "
1950
						+ ee.getMessage());
1951
			}//catch
1952
			finally {
1953
				DBConnectionPool.returnDBConnection(dbConn, serialNumber);
1954
			}//fianlly
1955
		}//finally
1956

    
1957
		return null;
1958
		//return null if the server does not exist
1959
	}
1960

    
1961
	/**
1962
	 * Returns a server code given a server name
1963
	 * @param server the name of the server
1964
	 * @return integer > 0 representing the code of the server, 0 if the server
1965
	 *  does not exist.
1966
	 */
1967
	public static int getServerCodeForServerName(String server) throws ServiceException {
1968
		DBConnection dbConn = null;
1969
		int serialNumber = -1;
1970
		PreparedStatement pstmt = null;
1971
		int serverCode = 0;
1972

    
1973
		try {
1974

    
1975
			//conn = util.openDBConnection();
1976
			dbConn = DBConnectionPool.getDBConnection("MetacatReplication.getServerCode");
1977
			serialNumber = dbConn.getCheckOutSerialNumber();
1978
			pstmt = dbConn.prepareStatement("SELECT serverid FROM xml_replication "
1979
					+ "WHERE server LIKE ?");
1980
			pstmt.setString(1, server);
1981
			pstmt.execute();
1982
			ResultSet rs = pstmt.getResultSet();
1983
			boolean tablehasrows = rs.next();
1984
			if (tablehasrows) {
1985
				serverCode = rs.getInt(1);
1986
				pstmt.close();
1987
				//conn.close();
1988
				return serverCode;
1989
			}
1990

    
1991
		} catch (SQLException sqle) {
1992
			throw new ServiceException("ReplicationService.getServerCodeForServerName - " 
1993
					+ "SQL error when getting server code: " + sqle.getMessage());
1994

    
1995
		} finally {
1996
			try {
1997
				pstmt.close();
1998
				//conn.close();
1999
			}//try
2000
			catch (Exception ee) {
2001
				logMetacat.error("ReplicationService.getServerCodeForServerName - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
2002
				logReplication.error("ReplicationService.getServerNameForServerCode - Error in MetacatReplicatio.getServerCode: "
2003
						+ ee.getMessage());
2004

    
2005
			}//catch
2006
			finally {
2007
				DBConnectionPool.returnDBConnection(dbConn, serialNumber);
2008
			}//finally
2009
		}//finally
2010

    
2011
		return serverCode;
2012
	}
2013

    
2014
	/**
2015
	 * Method to get a host server information for given docid
2016
	 * @param conn a connection to the database
2017
	 */
2018
	public static Hashtable<String, String> getHomeServerInfoForDocId(String docId) {
2019
		Hashtable<String, String> sl = new Hashtable<String, String>();
2020
		DBConnection dbConn = null;
2021
		int serialNumber = -1;
2022
		docId = DocumentUtil.getDocIdFromString(docId);
2023
		PreparedStatement pstmt = null;
2024
		int serverLocation;
2025
		try {
2026
			//get conection
2027
			dbConn = DBConnectionPool.getDBConnection("ReplicationHandler.getHomeServer");
2028
			serialNumber = dbConn.getCheckOutSerialNumber();
2029
			//get a server location from xml_document table
2030
			pstmt = dbConn.prepareStatement("select server_location from xml_documents "
2031
					+ "where docid = ?");
2032
			pstmt.setString(1, docId);
2033
			pstmt.execute();
2034
			ResultSet serverName = pstmt.getResultSet();
2035
			//get a server location
2036
			if (serverName.next()) {
2037
				serverLocation = serverName.getInt(1);
2038
				pstmt.close();
2039
			} else {
2040
				pstmt.close();
2041
				//ut.returnConnection(conn);
2042
				return null;
2043
			}
2044
			pstmt = dbConn.prepareStatement("select server, last_checked, replicate "
2045
					+ "from xml_replication where serverid = ?");
2046
			//increase usage count
2047
			dbConn.increaseUsageCount(1);
2048
			pstmt.setInt(1, serverLocation);
2049
			pstmt.execute();
2050
			ResultSet rs = pstmt.getResultSet();
2051
			boolean tableHasRows = rs.next();
2052
			if (tableHasRows) {
2053

    
2054
				String server = rs.getString(1);
2055
				String last_checked = rs.getString(2);
2056
				if (!server.equals("localhost")) {
2057
					sl.put(server, last_checked);
2058
				}
2059

    
2060
			} else {
2061
				pstmt.close();
2062
				//ut.returnConnection(conn);
2063
				return null;
2064
			}
2065
			pstmt.close();
2066
		} catch (Exception e) {
2067
			logMetacat.error("ReplicationService.getHomeServerInfoForDocId - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
2068
			logReplication.error("ReplicationService.getHomeServerInfoForDocId - error in replicationHandler.getHomeServer(): "
2069
					+ e.getMessage());
2070
		} finally {
2071
			try {
2072
				pstmt.close();
2073
				//ut.returnConnection(conn);
2074
			} catch (Exception ee) {
2075
				logMetacat.error("ReplicationService.getHomeServerInfoForDocId - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
2076
				logReplication.error("ReplicationService.getHomeServerInfoForDocId - Eror irn rplicationHandler.getHomeServer() "
2077
						+ "to close pstmt: " + ee.getMessage());
2078
			} finally {
2079
				DBConnectionPool.returnDBConnection(dbConn, serialNumber);
2080
			}
2081

    
2082
		}//finally
2083
		return sl;
2084
	}
2085

    
2086
	/**
2087
	 * Returns a home server location  given a accnum
2088
	 * @param accNum , given accNum for a document
2089
	 *
2090
	 */
2091
	public static int getHomeServerCodeForDocId(String accNum) throws ServiceException {
2092
		DBConnection dbConn = null;
2093
		int serialNumber = -1;
2094
		PreparedStatement pstmt = null;
2095
		int serverCode = 1;
2096
		String docId = DocumentUtil.getDocIdFromString(accNum);
2097

    
2098
		try {
2099

    
2100
			// Get DBConnection
2101
			dbConn = DBConnectionPool
2102
					.getDBConnection("ReplicationHandler.getServerLocation");
2103
			serialNumber = dbConn.getCheckOutSerialNumber();
2104
			pstmt = dbConn.prepareStatement("SELECT server_location FROM xml_documents "
2105
					+ "WHERE docid LIKE ? ");
2106
			pstmt.setString(1, docId);
2107
			pstmt.execute();
2108
			ResultSet rs = pstmt.getResultSet();
2109
			boolean tablehasrows = rs.next();
2110
			//If a document is find, return the server location for it
2111
			if (tablehasrows) {
2112
				serverCode = rs.getInt(1);
2113
				pstmt.close();
2114
				//conn.close();
2115
				return serverCode;
2116
			}
2117
			//if couldn't find in xml_documents table, we think server code is 1
2118
			//(this is new document)
2119
			else {
2120
				pstmt.close();
2121
				//conn.close();
2122
				return serverCode;
2123
			}
2124

    
2125
		} catch (SQLException sqle) {
2126
			throw new ServiceException("ReplicationService.getHomeServerCodeForDocId - " 
2127
					+ "SQL error when getting home server code for docid: " + docId + " : " 
2128
					+ sqle.getMessage());
2129

    
2130
		} finally {
2131
			try {
2132
				pstmt.close();
2133
				//conn.close();
2134

    
2135
			} catch (SQLException sqle) {
2136
				logMetacat.error("ReplicationService.getHomeServerCodeForDocId - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
2137
				logReplication.error("ReplicationService.getHomeServerCodeForDocId - ReplicationService.getHomeServerCodeForDocId - " 
2138
						+ "SQL error when getting home server code for docid: " + docId + " : " 
2139
						+ sqle.getMessage());
2140
			} finally {
2141
				DBConnectionPool.returnDBConnection(dbConn, serialNumber);
2142
			}//finally
2143
		}//finally
2144
		//return serverCode;
2145
	}
2146

    
2147
	/**
2148
	 * This method returns the content of a url
2149
	 * @param u the url to return the content from
2150
	 * @return a string representing the content of the url
2151
	 * @throws java.io.IOException
2152
	 */
2153
	public static String getURLContent(URL u) throws java.io.IOException {
2154
		char istreamChar;
2155
		int istreamInt;
2156
		// get the response content
2157
		InputStream input = getURLStream(u);
2158
		logReplication.info("ReplicationService.getURLContent - After getting response from: " + u.toString());
2159
		InputStreamReader istream = new InputStreamReader(input);
2160
		StringBuffer serverResponse = new StringBuffer();
2161
		while ((istreamInt = istream.read()) != -1) {
2162
			istreamChar = (char) istreamInt;
2163
			serverResponse.append(istreamChar);
2164
		}
2165
		istream.close();
2166
		input.close();
2167

    
2168
		return serverResponse.toString();
2169
	}
2170
	
2171
	/**
2172
	 * This method returns the InputStream after opening a url
2173
	 * @param u the url to return the content from
2174
	 * @return a InputStream representing the content of the url
2175
	 * @throws java.io.IOException
2176
	 */
2177
	public static InputStream getURLStream(URL u) throws java.io.IOException {
2178
	    logReplication.info("Getting url stream from " + u.toString());
2179
		logReplication.info("ReplicationService.getURLStream - Before sending request to: " + u.toString());
2180
		// use httpclient to set up SSL
2181
		RestClient client = getSSLClient();
2182
		HttpResponse response = client.doGetRequest(u.toString());
2183
		// get the response content
2184
		InputStream input = response.getEntity().getContent();
2185
		logReplication.info("ReplicationService.getURLStream - After getting response from: " + u.toString());
2186
		
2187
		return input;		
2188
	}
2189
	
2190
	/**
2191
	 * Sets up an HttpClient with SSL connection.
2192
	 * Sends client certificate to the server when doing the request.
2193
	 * @return
2194
	 */
2195
	private static RestClient getSSLClient() {
2196
		RestClient client = new RestClient();
2197
		
2198
		// set up this server's client identity
2199
		String subject = null;
2200
		try {
2201
			// TODO: should there be alternative ways to get the key and certificate?
2202
			String certificateFile = PropertyService.getProperty("replication.certificate.file");
2203
	    	String keyFile = PropertyService.getProperty("replication.privatekey.file");
2204
			String keyPassword = PropertyService.getProperty("replication.privatekey.password");
2205
			X509Certificate certificate = CertificateManager.getInstance().loadCertificateFromFile(certificateFile);
2206
			PrivateKey privateKey = CertificateManager.getInstance().loadPrivateKeyFromFile(keyFile, keyPassword);
2207
			subject = CertificateManager.getInstance().getSubjectDN(certificate);
2208
			CertificateManager.getInstance().registerCertificate(subject, certificate, privateKey);
2209
		} catch (Exception e) {
2210
			// this is pretty much required for replication communication
2211
			logReplication.warn("Could not find server's client certificate/private key: " + e.getMessage());
2212
		}
2213
		
2214
		// set the configured timeout
2215
		client.setTimeouts(CLIENTTIMEOUT);
2216

    
2217
		SSLSocketFactory socketFactory = null;
2218
		try {
2219
			socketFactory = CertificateManager.getInstance().getSSLSocketFactory(subject);
2220
		} catch (FileNotFoundException e) {
2221
			// these are somewhat expected for anonymous client use
2222
			logReplication.warn("Could not set up SSL connection for client - likely because the certificate could not be located: " + e.getMessage());
2223
		} catch (Exception e) {
2224
			// this is likely more severe
2225
			logReplication.warn("Funky SSL going on: " + e.getClass() + ":: " + e.getMessage());
2226
		}
2227
		try {
2228
			//443 is the default port, this value is overridden if explicitly set in the URL
2229
			Scheme sch = new Scheme("https", 443, socketFactory);
2230
			client.getHttpClient().getConnectionManager().getSchemeRegistry().register(sch);
2231
		} catch (Exception e) {
2232
			// this is likely more severe
2233
			logReplication.error("Failed to set up SSL connection for client. Continuing. " + e.getClass() + ":: " + e.getMessage(), e);
2234
		}
2235
		return client;
2236
	}
2237
	
2238

    
2239
//	/**
2240
//	 * Method for writing replication messages to a log file specified in
2241
//	 * metacat.properties
2242
//	 */
2243
//	public static void replLog(String message) {
2244
//		try {
2245
//			FileOutputStream fos = new FileOutputStream(PropertyService
2246
//					.getProperty("replication.logdir")
2247
//					+ "/metacatreplication.log", true);
2248
//			PrintWriter pw = new PrintWriter(fos);
2249
//			SimpleDateFormat formatter = new SimpleDateFormat("yy-MM-dd HH:mm:ss");
2250
//			java.util.Date localtime = new java.util.Date();
2251
//			String dateString = formatter.format(localtime);
2252
//			dateString += " :: " + message;
2253
//			// time stamp each entry
2254
//			pw.println(dateString);
2255
//			pw.flush();
2256
//		} catch (Exception e) {
2257
//			logReplication.error("error writing to replication log from "
2258
//					+ "MetacatReplication.replLog: " + e.getMessage());
2259
//			// e.printStackTrace(System.out);
2260
//		}
2261
//	}
2262

    
2263
//	/**
2264
//	 * Method for writing replication messages to a log file specified in
2265
//	 * metacat.properties
2266
//	 */
2267
//	public static void replErrorLog(String message) {
2268
//		try {
2269
//			FileOutputStream fos = new FileOutputStream(PropertyService
2270
//					.getProperty("replication.logdir")
2271
//					+ "/metacatreplicationerror.log", true);
2272
//			PrintWriter pw = new PrintWriter(fos);
2273
//			SimpleDateFormat formatter = new SimpleDateFormat("yy-MM-dd HH:mm:ss");
2274
//			java.util.Date localtime = new java.util.Date();
2275
//			String dateString = formatter.format(localtime);
2276
//			dateString += " :: " + message;
2277
//			//time stamp each entry
2278
//			pw.println(dateString);
2279
//			pw.flush();
2280
//		} catch (Exception e) {
2281
//			logReplication.error("error writing to replication error log from "
2282
//					+ "MetacatReplication.replErrorLog: " + e.getMessage());
2283
//			//e.printStackTrace(System.out);
2284
//		}
2285
//	}
2286

    
2287
	/**
2288
	 * Returns true if the replicate field for server in xml_replication is 1.
2289
	 * Returns false otherwise
2290
	 */
2291
	public static boolean replToServer(String server) {
2292
		DBConnection dbConn = null;
2293
		int serialNumber = -1;
2294
		PreparedStatement pstmt = null;
2295
		try {
2296
			dbConn = DBConnectionPool.getDBConnection("MetacatReplication.repltoServer");
2297
			serialNumber = dbConn.getCheckOutSerialNumber();
2298
			pstmt = dbConn.prepareStatement("select replicate from "
2299
					+ "xml_replication where server like ? ");
2300
			pstmt.setString(1, server);
2301
			pstmt.execute();
2302
			ResultSet rs = pstmt.getResultSet();
2303
			boolean tablehasrows = rs.next();
2304
			if (tablehasrows) {
2305
				int i = rs.getInt(1);
2306
				if (i == 1) {
2307
					pstmt.close();
2308
					//conn.close();
2309
					return true;
2310
				} else {
2311
					pstmt.close();
2312
					//conn.close();
2313
					return false;
2314
				}
2315
			}
2316
		} catch (SQLException sqle) {
2317
			logMetacat.error("ReplicationService.replToServer - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
2318
			logReplication.error("ReplicationService.replToServer - SQL error in MetacatReplication.replToServer: "
2319
					+ sqle.getMessage());
2320
		} finally {
2321
			try {
2322
				pstmt.close();
2323
				//conn.close();
2324
			}//try
2325
			catch (Exception ee) {
2326
				logMetacat.error("ReplicationService.replToServer - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
2327
				logReplication.error("ReplicationService.replToServer - Error in MetacatReplication.replToServer: "
2328
						+ ee.getMessage());
2329
			}//catch
2330
			finally {
2331
				DBConnectionPool.returnDBConnection(dbConn, serialNumber);
2332
			}//finally
2333
		}//finally
2334
		return false;
2335
		//the default if this server does not exist is to not replicate to it.
2336
	}
2337

    
2338
}
(6-6/7)