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: jones $'
9
 *     '$Date: 2013-10-09 23:52:11 -0700 (Wed, 09 Oct 2013) $'
10
 * '$Revision: 8304 $'
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.Calendar;
50
import java.util.Date;
51
import java.util.Hashtable;
52
import java.util.Timer;
53
import java.util.Vector;
54

    
55
import javax.servlet.http.HttpServletRequest;
56
import javax.servlet.http.HttpServletResponse;
57

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

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

    
104
public class ReplicationService extends BaseService {
105

    
106
	private static ReplicationService replicationService = null;
107

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

    
124
	public static final String REPLICATION_LOG_FILE_NAME = "metacatreplication.log";
125

    
126
	private static String DATA_FILE_FLAG = null;
127
	public static String METACAT_REPL_ERROR_MSG = null;
128
	private static Logger logReplication = Logger.getLogger("ReplicationLogging");
129
	private static Logger logMetacat = Logger.getLogger(ReplicationService.class);
130

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

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

    
173
			String timeIntervalStr = 
174
				PropertyService.getProperty("replication.timedreplicationinterval");
175
			timeInterval = (new Long(timeIntervalStr)).longValue();
176
			logReplication.info("ReplicationService.initialize - The timed replication time Interval is " + timeInterval);
177

    
178
			String firstTimeStr = 
179
				PropertyService.getProperty("replication.firsttimedreplication");
180
			logReplication.info("ReplicationService.initialize - first replication time form property is " + firstTimeStr);
181
			firstTime = ReplicationHandler.combinateCurrentDateAndGivenTime(firstTimeStr);
182

    
183
			logReplication.info("ReplicationService.initialize - After combine current time, the real first time is "
184
					+ firstTime.toString() + " minisec");
185

    
186
			// set up time replication if it is on
187
			if (timedReplicationIsOn) {
188
				replicationDaemon.scheduleAtFixedRate(new ReplicationHandler(),
189
						firstTime, timeInterval);
190
				logReplication.info("ReplicationService.initialize - deltaT handler started with rate="
191
						+ timeInterval + " mini seconds at " + firstTime.toString());
192
			}
193

    
194
		} catch (PropertyNotFoundException pnfe) {
195
			throw new ServiceException(
196
					"ReplicationService.initialize - Property error while instantiating "
197
							+ "replication service: " + pnfe.getMessage());
198
		} catch (HandlerException he) {
199
			throw new ServiceException(
200
					"ReplicationService.initialize - Handler error while instantiating "
201
							+ "replication service" + he.getMessage());
202
		} 
203
	}
204

    
205
	/**
206
	 * Get the single instance of SessionService.
207
	 * 
208
	 * @return the single instance of SessionService
209
	 */
210
	public static ReplicationService getInstance() throws ServiceException {
211
		if (replicationService == null) {
212
			replicationService = new ReplicationService();
213
		}
214
		return replicationService;
215
	}
216

    
217
	public boolean refreshable() {
218
		return true;
219
	}
220

    
221
	protected void doRefresh() throws ServiceException {
222
		return;
223
	}
224
	
225
	public void stop() throws ServiceException{
226
		
227
	}
228

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

    
240
	      logReplication.info("ReplicationService.stopReplication - deltaT handler stopped");
241
		return;
242
	}
243
	
244
	public void startReplication(Hashtable<String, String[]> params) throws ServiceException {
245

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

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

    
340
			// add server to server list
341
			if (subaction.equals("add")) {
342
				replicate = ((String[]) params.get("replicate"))[0];
343
				server = ((String[]) params.get("server"))[0];
344

    
345
				//Get data replication value
346
				dataReplicate = ((String[]) params.get("datareplicate"))[0];
347
				
348
				//Get hub value
349
				hub = ((String[]) params.get("hub"))[0];
350

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

    
390
			} else if (subaction.equals("generatesystemmetadata")) {
391
				GenerateSystemMetadata gsm = new GenerateSystemMetadata();
392
				int serverLocation = -1;
393
				String serverid = ((String[]) params.get("serverid"))[0];
394
				serverLocation = Integer.parseInt(serverid);
395
				gsm.setServerLocation(serverLocation );
396
				gsm.multiThreadUpgrade();
397
				out.write("System Metadata generated for server " + serverid);
398
				
399
			} else if (subaction.equals("generateore")) {
400
				GenerateORE gore = new GenerateORE();
401
				int serverLocation = -1;
402
				String serverid = ((String[]) params.get("serverid"))[0];
403
				serverLocation = Integer.parseInt(serverid);
404
				gore.setServerLocation(serverLocation );
405
				gore.upgrade();
406
				out.write("Generated ORE maps for server " + serverid);
407
				
408
			} else if (subaction.equals("removeinvalidreplicas")) {
409
				RemoveInvalidReplicas rir = new RemoveInvalidReplicas();
410
				int serverLocation = -1;
411
				String serverid = ((String[]) params.get("serverid"))[0];
412
				serverLocation = Integer.parseInt(serverid);
413
				rir.setServerLocation(serverLocation );
414
				rir.upgrade();
415
				out.write("Removed invalid replicas for server " + serverid);
416
				
417
			} else {
418
			
419
				out.write("<error>Unkonwn subaction</error>");
420
				return;
421
			}
422
			
423
			// show SM generate button?
424
			String dataoneConfigured = PropertyService.getProperty("configutil.dataoneConfigured");
425
			if (dataoneConfigured != null) {
426
				showGenerateSystemMetadata = Boolean.parseBoolean(dataoneConfigured);
427
			}
428
			
429
			// always list them after processing
430
			response.setContentType("text/html");
431
			out.write("<html><body><table border=\"1\">");
432
			out.write("<tr><td><b>server</b></td>");
433
			out.write("<td><b>last_checked</b></td>");
434
			out.write("<td><b>replicate</b></td>");
435
			out.write("<td><b>datareplicate</b></td>");
436
			out.write("<td><b>hub</b></td>");
437
			if (showGenerateSystemMetadata) {
438
				out.write("<td><b>System Metadata</b></td>");
439
				out.write("<td><b>ORE Maps</b></td>");
440
				out.write("<td><b>Invalid Replicas</b></td>");
441
			}
442
			out.write("</tr>");
443

    
444
			pstmt = dbConn.prepareStatement("SELECT serverid, server, last_checked, replicate, datareplicate, hub FROM xml_replication");
445
			pstmt.execute();
446
			ResultSet rs = pstmt.getResultSet();
447
			boolean tablehasrows = rs.next();
448
			while (tablehasrows) {
449
				String serverId = rs.getString(1);
450
				out.write("<tr><td>" + rs.getString(2) + "</td><td>");
451
				out.write(rs.getString(3) + "</td><td>");
452
				out.write(rs.getString(4) + "</td><td>");
453
				out.write(rs.getString(5) + "</td><td>");
454
				out.write(rs.getString(6) + "</td>");
455
				if (showGenerateSystemMetadata) {
456
					// for SM
457
					out.write("<td><form action='" + request.getContextPath() + "/admin'>");
458
					out.write("<input name='serverid' type='hidden' value='" + serverId  + "'/>");
459
					out.write("<input name='configureType' type='hidden' value='replication'/>");
460
					out.write("<input name='action' type='hidden' value='servercontrol'/>");
461
					out.write("<input name='subaction' type='hidden' value='generatesystemmetadata'/>");
462
					out.write("<input type='submit' value='Generate System Metadata'/>");
463
					out.write("</form></td>");
464
					
465
					// for ORE maps
466
					out.write("<td><form action='" + request.getContextPath() + "/admin'>");
467
					out.write("<input name='serverid' type='hidden' value='" + serverId + "'/>");
468
					out.write("<input name='configureType' type='hidden' value='replication'/>");
469
					out.write("<input name='action' type='hidden' value='servercontrol'/>");
470
					out.write("<input name='subaction' type='hidden' value='generateore'/>");
471
					out.write("<input type='submit' value='Generate ORE'/>");
472
					out.write("</form></td>");
473
					
474
					// for invalid replicas
475
					out.write("<td><form action='" + request.getContextPath() + "/admin'>");
476
					out.write("<input name='serverid' type='hidden' value='" + serverId + "'/>");
477
					out.write("<input name='configureType' type='hidden' value='replication'/>");
478
					out.write("<input name='action' type='hidden' value='servercontrol'/>");
479
					out.write("<input name='subaction' type='hidden' value='removeinvalidreplicas'/>");
480
					String disabled = "";
481
					if (Integer.valueOf(serverId) == 1) {
482
						disabled = "disabled='true'";
483
					}
484
					out.write("<input type='submit' value='Remove Invalid Replicas' " + disabled + " />");
485
					out.write("</form></td>");
486
				}
487
				out.write("</tr>");
488

    
489
				tablehasrows = rs.next();
490
			}
491
			out.write("</table></body></html>");
492
			
493
			
494
			pstmt.close();
495
			//conn.close();
496

    
497
		} catch (Exception e) {
498
			logMetacat.error("ReplicationService.handleServerControlRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
499
			logReplication.error("ReplicationService.handleServerControlRequest - Error in "
500
					+ "MetacatReplication.handleServerControlRequest " + e.getMessage());
501
			e.printStackTrace(System.out);
502
		} finally {
503
			try {
504
				pstmt.close();
505
			}//try
506
			catch (SQLException ee) {
507
				logMetacat.error("ReplicationService.handleServerControlRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
508
				logReplication.error("ReplicationService.handleServerControlRequest - Error in MetacatReplication.handleServerControlRequest to close pstmt "
509
						+ ee.getMessage());
510
			}//catch
511
			finally {
512
				DBConnectionPool.returnDBConnection(dbConn, serialNumber);
513
			}//finally
514
			if (out != null) {
515
				try {
516
					out.close();
517
				} catch (IOException e) {
518
					logMetacat.error(e.getMessage(), e);
519
				}
520
			}
521
		}//finally
522

    
523
	}
524

    
525
	/**
526
	 * when a forcereplication request comes in, local host sends a read request
527
	 * to the requesting server (remote server) for the specified docid. Then
528
	 * store it in local database.
529
	 */
530
	protected static void handleForceReplicateRequest(
531
			Hashtable<String, String[]> params, HttpServletResponse response,
532
			HttpServletRequest request) {
533
		String server = ((String[]) params.get("server"))[0]; // the server that
534
		String docid = ((String[]) params.get("docid"))[0]; // sent the document
535
		String dbaction = "UPDATE"; // the default action is UPDATE
536
		//    boolean override = false;
537
		//    int serverCode = 1;
538
		DBConnection dbConn = null;
539
		int serialNumber = -1;
540
		String docName = null;
541

    
542
		try {
543
			//if the url contains a dbaction then the default action is overridden
544
			if (params.containsKey("dbaction")) {
545
				dbaction = ((String[]) params.get("dbaction"))[0];
546
				//serverCode = MetacatReplication.getServerCode(server);
547
				//override = true; //we are now overriding the default action
548
			}
549
			logReplication.info("ReplicationService.handleForceReplicateRequest - Force replication request from: " + server);
550
			logReplication.info("ReplicationService.handleForceReplicateRequest - Force replication docid: " + docid);
551
			logReplication.info("ReplicationService.handleForceReplicateRequest - Force replication action: " + dbaction);
552
			// sending back read request to remote server
553
			URL u = new URL("https://" + server + "?server="
554
					+ MetacatUtil.getLocalReplicationServerName() + "&action=read&docid="
555
					+ docid);
556
			String xmldoc = ReplicationService.getURLContent(u);
557

    
558
			// get the document info from server
559
			URL docinfourl = new URL("https://" + server + "?server="
560
					+ MetacatUtil.getLocalReplicationServerName()
561
					+ "&action=getdocumentinfo&docid=" + docid);
562
			
563

    
564
			String docInfoStr = ReplicationService.getURLContent(docinfourl);
565
			// strip out the system metadata portion
566
			String systemMetadataXML = ReplicationUtil.getSystemMetadataContent(docInfoStr);
567
			docInfoStr = ReplicationUtil.getContentWithoutSystemMetadata(docInfoStr);
568
		   	  
569
			//dih is the parser for the docinfo xml format
570
			DocInfoHandler dih = new DocInfoHandler();
571
			XMLReader docinfoParser = ReplicationHandler.initParser(dih);
572
			docinfoParser.parse(new InputSource(new StringReader(docInfoStr)));
573
			//      Hashtable<String,Vector<AccessControlForSingleFile>> docinfoHash = dih.getDocInfo();
574
			Hashtable<String, String> docinfoHash = dih.getDocInfo();
575
			
576
			// Get home server of this docid
577
			String homeServer = (String) docinfoHash.get("home_server");
578
			
579
			// process system metadata
580
			if (systemMetadataXML != null) {
581
				SystemMetadata sysMeta = 
582
					TypeMarshaller.unmarshalTypeFromStream(
583
							SystemMetadata.class,
584
							new ByteArrayInputStream(systemMetadataXML.getBytes("UTF-8")));
585
				// need the guid-to-docid mapping
586
				boolean mappingExists = true;
587
		      	mappingExists = IdentifierManager.getInstance().mappingExists(sysMeta.getIdentifier().getValue());
588
		      	if (!mappingExists) {
589
		      		IdentifierManager.getInstance().createMapping(sysMeta.getIdentifier().getValue(), docid);
590
		      	}
591
				// save the system metadata
592
				HazelcastService.getInstance().getSystemMetadataMap().put(sysMeta.getIdentifier(), sysMeta);
593
				// submit for indexing
594
                HazelcastService.getInstance().getIndexQueue().add(sysMeta);
595
			}
596
      
597
			// dates
598
			String createdDateString = docinfoHash.get("date_created");
599
			String updatedDateString = docinfoHash.get("date_updated");
600
			Date createdDate = DateTimeMarshaller.deserializeDateToUTC(createdDateString);
601
			Date updatedDate = DateTimeMarshaller.deserializeDateToUTC(updatedDateString);
602
		      
603
			logReplication.info("ReplicationService.handleForceReplicateRequest - homeServer: " + homeServer);
604
			// Get Document type
605
			String docType = (String) docinfoHash.get("doctype");
606
			logReplication.info("ReplicationService.handleForceReplicateRequest - docType: " + docType);
607
			String parserBase = null;
608
			// this for eml2 and we need user eml2 parser
609
			if (docType != null
610
					&& (docType.trim()).equals(DocumentImpl.EML2_0_0NAMESPACE)) {
611
				logReplication.warn("ReplicationService.handleForceReplicateRequest - This is an eml200 document!");
612
				parserBase = DocumentImpl.EML200;
613
			} else if (docType != null
614
					&& (docType.trim()).equals(DocumentImpl.EML2_0_1NAMESPACE)) {
615
				logReplication.warn("ReplicationService.handleForceReplicateRequest - This is an eml2.0.1 document!");
616
				parserBase = DocumentImpl.EML200;
617
			} else if (docType != null
618
					&& (docType.trim()).equals(DocumentImpl.EML2_1_0NAMESPACE)) {
619
				logReplication.warn("ReplicationService.handleForceReplicateRequest - This is an eml2.1.0 document!");
620
				parserBase = DocumentImpl.EML210;
621
			} else if (docType != null
622
					&& (docType.trim()).equals(DocumentImpl.EML2_1_1NAMESPACE)) {
623
				logReplication.warn("ReplicationService.handleForceReplicateRequest - This is an eml2.1.1 document!");
624
				parserBase = DocumentImpl.EML210;
625
			}
626
			logReplication.warn("ReplicationService.handleForceReplicateRequest - The parserBase is: " + parserBase);
627

    
628
			// Get DBConnection from pool
629
			dbConn = DBConnectionPool
630
					.getDBConnection("MetacatReplication.handleForceReplicateRequest");
631
			serialNumber = dbConn.getCheckOutSerialNumber();
632
			// write the document to local database
633
			DocumentImplWrapper wrapper = new DocumentImplWrapper(parserBase, false, false);
634
			//try this independently so we can set access even if the update action is invalid (i.e docid has not changed)
635
			try {
636
				wrapper.writeReplication(dbConn, xmldoc, null, null,
637
						dbaction, docid, null, null, homeServer, server, createdDate,
638
						updatedDate);
639
			} finally {
640

    
641
				//process extra access rules before dealing with the write exception (doc exist already)
642
				try {
643
		        	// check if we had a guid -> docid mapping
644
		        	String docidNoRev = DocumentUtil.getDocIdFromAccessionNumber(docid);
645
		        	int rev = DocumentUtil.getRevisionFromAccessionNumber(docid);
646
		        	IdentifierManager.getInstance().getGUID(docidNoRev, rev);
647
		        	// no need to create the mapping if we have it
648
		        } catch (McdbDocNotFoundException mcdbe) {
649
		        	// create mapping if we don't
650
		        	IdentifierManager.getInstance().createMapping(docid, docid);
651
		        }
652
		        Vector<XMLAccessDAO> accessControlList = dih.getAccessControlList();
653
		        if (accessControlList != null) {
654
		        	AccessControlForSingleFile acfsf = new AccessControlForSingleFile(docid);
655
		        	for (XMLAccessDAO xmlAccessDAO : accessControlList) {
656
		        		try {
657
			        		if (!acfsf.accessControlExists(xmlAccessDAO)) {
658
			        			acfsf.insertPermissions(xmlAccessDAO);
659
								logReplication.info("ReplicationService.handleForceReplicateRequest - document " + docid
660
										+ " permissions added to DB");
661
			        		}
662
		        		} catch (PermOrderException poe) {
663
		        			// this is problematic, but should not prevent us from replicating
664
		        			// see https://redmine.dataone.org/issues/2583
665
		        			String msg = "Could not insert access control for: " + docid + " Message: " + poe.getMessage();
666
		        			logMetacat.error(msg, poe);
667
		        			logReplication.error(msg, poe);
668
		        		}
669
		            }
670
		        }
671
		        
672
		        // process the real owner and updater
673
				String user = (String) docinfoHash.get("user_owner");
674
				String updated = (String) docinfoHash.get("user_updated");
675
		        updateUserOwner(dbConn, docid, user, updated);
676

    
677
				logReplication.info("ReplicationService.handleForceReplicateRequest - document " + docid + " added to DB with "
678
						+ "action " + dbaction);
679
				
680
				EventLog.getInstance().log(request.getRemoteAddr(), request.getHeader("User-Agent"), REPLICATIONUSER, docid, dbaction);
681
			}
682
		} catch (SQLException sqle) {
683
			logMetacat.error("ReplicationService.handleForceReplicateRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
684
			logReplication.error("ReplicationService.handleForceReplicateRequest - SQL error when adding doc " + docid + 
685
					" to DB with action " + dbaction + ": " + sqle.getMessage());
686
		} catch (MalformedURLException mue) {
687
			logMetacat.error("ReplicationService.handleForceReplicateRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
688
			logReplication.error("ReplicationService.handleForceReplicateRequest - URL error when adding doc " + docid + 
689
					" to DB with action " + dbaction + ": " + mue.getMessage());
690
		} catch (SAXException se) {
691
			logMetacat.error("ReplicationService.handleForceReplicateRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
692
			logReplication.error("ReplicationService.handleForceReplicateRequest - SAX parsing error when adding doc " + docid + 
693
					" to DB with action " + dbaction + ": " + se.getMessage());
694
		} catch (HandlerException he) {
695
			logMetacat.error("ReplicationService.handleForceReplicateRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
696
			logReplication.error("ReplicationService.handleForceReplicateRequest - Handler error when adding doc " + docid + 
697
					" to DB with action " + dbaction + ": " + he.getMessage());
698
		} catch (IOException ioe) {
699
			logMetacat.error("ReplicationService.handleForceReplicateRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
700
			logReplication.error("ReplicationService.handleForceReplicateRequest - I/O error when adding doc " + docid + 
701
					" to DB with action " + dbaction + ": " + ioe.getMessage());
702
		} catch (PermOrderException poe) {
703
			logMetacat.error("ReplicationService.handleForceReplicateRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
704
			logReplication.error("ReplicationService.handleForceReplicateRequest - Permissions order error when adding doc " + docid + 
705
					" to DB with action " + dbaction + ": " + poe.getMessage());
706
		} catch (AccessControlException ace) {
707
			logMetacat.error("ReplicationService.handleForceReplicateRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
708
			logReplication.error("ReplicationService.handleForceReplicateRequest - Permissions order error when adding doc " + docid + 
709
					" to DB with action " + dbaction + ": " + ace.getMessage());
710
		} catch (Exception e) {
711
			logMetacat.error("ReplicationService.handleForceReplicateRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
712
			logReplication.error("ReplicationService.handleForceReplicateRequest - General error when adding doc " + docid + 
713
					" to DB with action " + dbaction + ": " + e.getMessage());
714
		} finally {
715
			// Return the checked out DBConnection
716
			DBConnectionPool.returnDBConnection(dbConn, serialNumber);
717
		}//finally
718
	}
719

    
720
	/*
721
	 * when a forcereplication delete request comes in, local host will delete this
722
	 * document
723
	 */
724
	protected static void handleForceReplicateDeleteRequest(
725
			Hashtable<String, String[]> params, HttpServletResponse response,
726
			HttpServletRequest request, boolean removeAll) {
727
		String server = ((String[]) params.get("server"))[0]; // the server that
728
		String docid = ((String[]) params.get("docid"))[0]; // sent the document
729
		try {
730
			logReplication.info("ReplicationService.handleForceReplicateDeleteRequest - force replication delete request from " + server);
731
			logReplication.info("ReplicationService.handleForceReplicateDeleteRequest - force replication delete docid " + docid);
732
			logReplication.info("ReplicationService.handleForceReplicateDeleteRequest - Force replication delete request from: " + server);
733
			logReplication.info("ReplicationService.handleForceReplicateDeleteRequest - Force replication delete docid: " + docid);
734
			DocumentImpl.delete(docid, null, null, server, removeAll);
735
			logReplication.info("ReplicationService.handleForceReplicateDeleteRequest - document " + docid + " was successfully deleted ");
736
			EventLog.getInstance().log(request.getRemoteAddr(), request.getHeader("User-Agent"), REPLICATIONUSER, docid,
737
					"delete");
738
			logReplication.info("ReplicationService.handleForceReplicateDeleteRequest - document " + docid + " was successfully deleted ");
739
		} catch (McdbDocNotFoundException e) {
740
			logMetacat.error("ReplicationService.handleForceReplicateDeleteRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
741
			logReplication.error("document " + docid
742
					+ " failed to delete because " + e.getMessage());
743
			logReplication.error("ReplicationService.handleForceReplicateDeleteRequest - error: " + e.getMessage());
744
		} catch (InsufficientKarmaException e) {
745
			logMetacat.error("ReplicationService.handleForceReplicateDeleteRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
746
			logReplication.error("document " + docid
747
					+ " failed to delete because " + e.getMessage());
748
			logReplication.error("ReplicationService.handleForceReplicateDeleteRequest - error: " + e.getMessage());
749
		} catch (SQLException e) {
750
			logMetacat.error("ReplicationService.handleForceReplicateDeleteRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
751
			logReplication.error("document " + docid
752
					+ " failed to delete because " + e.getMessage());
753
			logReplication.error("ReplicationService.handleForceReplicateDeleteRequest - error: " + e.getMessage());
754
		} catch (Exception e) {
755
			logMetacat.error("ReplicationService.handleForceReplicateDeleteRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
756
			logReplication.error("document " + docid
757
					+ " failed to delete because " + e.getMessage());
758
			logReplication.error("ReplicationService.handleForceReplicateDeleteRequest - error: " + e.getMessage());
759

    
760
		}//catch
761

    
762
	}
763

    
764
	/**
765
	 * when a forcereplication data file request comes in, local host sends a
766
	 * readdata request to the requesting server (remote server) for the specified
767
	 * docid. Then store it in local database and file system
768
	 */
769
	protected static void handleForceReplicateDataFileRequest(Hashtable<String, String[]> params,
770
			HttpServletRequest request) {
771

    
772
		//make sure there is some parameters
773
		if (params.isEmpty()) {
774
			return;
775
		}
776
		// Get remote server
777
		String server = ((String[]) params.get("server"))[0];
778
		// the docid should include rev number
779
		String docid = ((String[]) params.get("docid"))[0];
780
		// Make sure there is a docid and server
781
		if (docid == null || server == null || server.equals("")) {
782
			logMetacat.error("ReplicationService.handleForceReplicateDataFileRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
783
			logReplication.error("ReplicationService.handleForceReplicateDataFileRequest - Didn't specify docid or server for replication");
784
			return;
785
		}
786

    
787
		// Overide or not
788
		//    boolean override = false;
789
		// dbaction - update or insert
790
		String dbaction = null;
791

    
792
		try {
793
			//docid was switch to two parts uinque code and rev
794
			//String uniqueCode=MetacatUtil.getDocIdFromString(docid);
795
			//int rev=MetacatUtil.getVersionFromString(docid);
796
			if (params.containsKey("dbaction")) {
797
				dbaction = ((String[]) params.get("dbaction"))[0];
798
			} else//default value is update
799
			{
800
//				dbaction = "update";
801
				dbaction = null;
802
			}
803

    
804
			logReplication.info("ReplicationService.handleForceReplicateDataFileRequest - Force replication request from: " + server);
805
			logReplication.info("ReplicationService.handleForceReplicateDataFileRequest - Force replication docid: " + docid);
806
			logReplication.info("ReplicationService.handleForceReplicateDataFileRequest - Force replication action: " + dbaction);
807
			// get the document info from server
808
			URL docinfourl = new URL("https://" + server + "?server="
809
					+ MetacatUtil.getLocalReplicationServerName()
810
					+ "&action=getdocumentinfo&docid=" + docid);
811

    
812
			String docInfoStr = ReplicationService.getURLContent(docinfourl);
813
			
814
			// strip out the system metadata portion
815
		    String systemMetadataXML = ReplicationUtil.getSystemMetadataContent(docInfoStr);
816
		   	docInfoStr = ReplicationUtil.getContentWithoutSystemMetadata(docInfoStr);
817

    
818
			//dih is the parser for the docinfo xml format
819
			DocInfoHandler dih = new DocInfoHandler();
820
			XMLReader docinfoParser = ReplicationHandler.initParser(dih);
821
			docinfoParser.parse(new InputSource(new StringReader(docInfoStr)));
822
			Hashtable<String, String> docinfoHash = dih.getDocInfo();
823
			
824
			String docName = (String) docinfoHash.get("docname");
825

    
826
			String docType = (String) docinfoHash.get("doctype");
827

    
828
			String docHomeServer = (String) docinfoHash.get("home_server");
829
			
830
			String createdDateString = docinfoHash.get("date_created");
831
			String updatedDateString = docinfoHash.get("date_updated");
832
			
833
			Date createdDate = DateTimeMarshaller.deserializeDateToUTC(createdDateString);
834
			Date updatedDate = DateTimeMarshaller.deserializeDateToUTC(updatedDateString);
835
			
836
			logReplication.info("ReplicationService.handleForceReplicateDataFileRequest - docHomeServer of datafile: " + docHomeServer);
837

    
838
			// in case we have a write exception, we still want to track access and sysmeta
839
			Exception writeException = null;
840

    
841
			// do we need the object content?
842
			if (dbaction != null && (dbaction.equals("insert") || dbaction.equals("update"))) {
843
				//Get data file and store it into local file system.
844
				// sending back readdata request to server
845
				URL url = new URL("https://" + server + "?server="
846
						+ MetacatUtil.getLocalReplicationServerName()
847
						+ "&action=readdata&docid=" + docid);
848
				String datafilePath = PropertyService
849
						.getProperty("application.datafilepath");
850

    
851
				InputStream inputStream = getURLStream(url);
852
				
853
				//register data file into xml_documents table and write data file
854
				//into file system
855
				try {
856
					DocumentImpl.writeDataFileInReplication(inputStream,
857
							datafilePath, docName, docType, docid, null, docHomeServer,
858
							server, DocumentImpl.DOCUMENTTABLE, false, createdDate,
859
							updatedDate);
860
				} catch (Exception e) {
861
					writeException = e;
862
				}
863

    
864
			}
865
			
866
			// process the real owner and updater
867
			DBConnection dbConn = DBConnectionPool.getDBConnection("ReplicationService.handleForceDataFileRequest");
868
	        int serialNumber = dbConn.getCheckOutSerialNumber();
869
	        dbConn.setAutoCommit(false);
870
			String user = (String) docinfoHash.get("user_owner");
871
			String updated = (String) docinfoHash.get("user_updated");
872
	        updateUserOwner(dbConn, docid, user, updated);
873
	        DBConnectionPool.returnDBConnection(dbConn, serialNumber);
874
	        
875
			// process system metadata
876
	        if (systemMetadataXML != null) {
877
	      	  SystemMetadata sysMeta = 
878
	      		TypeMarshaller.unmarshalTypeFromStream(
879
	      				  SystemMetadata.class, 
880
	      				  new ByteArrayInputStream(systemMetadataXML.getBytes("UTF-8")));
881
	      	  
882
	      	  // need the guid-to-docid mapping
883
	      	  boolean mappingExists = true;
884
	      	  mappingExists = IdentifierManager.getInstance().mappingExists(sysMeta.getIdentifier().getValue());
885
	      	  if (!mappingExists) {
886
	      		  IdentifierManager.getInstance().createMapping(sysMeta.getIdentifier().getValue(), docid);
887
	      	  }
888
	      	  // save the system metadata
889
	      	  HazelcastService.getInstance().getSystemMetadataMap().put(sysMeta.getIdentifier(), sysMeta);
890
	      	  // submit for indexing
891
              HazelcastService.getInstance().getIndexQueue().add(sysMeta);
892
	        }
893
	        
894
	        // process the access control
895
	        try {
896
	        	// check if we had a guid -> docid mapping
897
	        	String docidNoRev = DocumentUtil.getDocIdFromAccessionNumber(docid);
898
	        	int rev = DocumentUtil.getRevisionFromAccessionNumber(docid);
899
	        	IdentifierManager.getInstance().getGUID(docidNoRev, rev);
900
	        	// no need to create the mapping if we have it
901
	        } catch (McdbDocNotFoundException mcdbe) {
902
	        	// create mapping if we don't
903
	        	IdentifierManager.getInstance().createMapping(docid, docid);
904
	        }
905
	        Vector<XMLAccessDAO> accessControlList = dih.getAccessControlList();
906
	        if (accessControlList != null) {
907
	        	AccessControlForSingleFile acfsf = new AccessControlForSingleFile(docid);
908
	        	for (XMLAccessDAO xmlAccessDAO : accessControlList) {
909
	        		if (!acfsf.accessControlExists(xmlAccessDAO)) {
910
	        			acfsf.insertPermissions(xmlAccessDAO);
911
						logReplication.info("ReplicationService.handleForceReplicateRequest - document " + docid
912
								+ " permissions added to DB");
913
	        		}
914
	            }
915
	        }
916
	        
917
	        // throw the write exception now -- this happens when access changes on an object
918
			if (writeException != null) {
919
				throw writeException;
920
			}
921

    
922
			logReplication.info("ReplicationService.handleForceReplicateDataFileRequest - datafile " + docid + " added to DB with "
923
					+ "action " + dbaction);
924
			EventLog.getInstance().log(request.getRemoteAddr(), request.getHeader("User-Agent"), REPLICATIONUSER,
925
					docid, dbaction);
926

    
927
		} catch (Exception e) {
928
			e.printStackTrace();
929
			logMetacat.error("ReplicationService.handleForceReplicateDataFileRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG, e);                         
930
			logReplication.error("ReplicationService.handleForceReplicateDataFileRequest - Datafile " + docid
931
					+ " failed to added to DB with " + "action " + dbaction + " because "
932
					+ e.getMessage());
933
			logReplication.error("ReplicationService.handleForceReplicateDataFileRequest - ERROR in MetacatReplication.handleForceDataFileReplicate"
934
					+ "Request(): " + e.getMessage());
935
		}
936
	}
937

    
938
	/**
939
	 * Grants or denies a lock to a requesting host.
940
	 * The servlet parameters of interrest are:
941
	 * docid: the docid of the file the lock is being requested for
942
	 * currentdate: the timestamp of the document on the remote server
943
	 *
944
	 */
945
	protected static void handleGetLockRequest(
946
			Hashtable<String, String[]> params, HttpServletResponse response) {
947

    
948
		try {
949

    
950
			String docid = ((String[]) params.get("docid"))[0];
951
			String remoteRev = ((String[]) params.get("updaterev"))[0];
952
			DocumentImpl requestDoc = new DocumentImpl(docid);
953
			logReplication.info("ReplicationService.handleGetLockRequest - lock request for " + docid);
954
			int localRevInt = requestDoc.getRev();
955
			int remoteRevInt = Integer.parseInt(remoteRev);
956

    
957
			// get a writer for sending back to response
958
			response.setContentType("text/xml");
959
			Writer out = response.getWriter();
960
			
961
			if (remoteRevInt >= localRevInt) {
962
				if (!fileLocks.contains(docid)) { //grant the lock if it is not already locked
963
					fileLocks.add(0, docid); //insert at the beginning of the queue Vector
964
					//send a message back to the the remote host authorizing the insert
965
					out.write("<lockgranted><docid>" + docid
966
									+ "</docid></lockgranted>");
967
					//          lockThread = new Thread(this);
968
					//          lockThread.setPriority(Thread.MIN_PRIORITY);
969
					//          lockThread.start();
970
					logReplication.info("ReplicationService.handleGetLockRequest - lock granted for " + docid);
971
				} else { //deny the lock
972
					out.write("<filelocked><docid>" + docid + "</docid></filelocked>");
973
					logReplication.info("ReplicationService.handleGetLockRequest - lock denied for " + docid
974
							+ "reason: file already locked");
975
				}
976
			} else {//deny the lock.
977
				out.write("<outdatedfile><docid>" + docid + "</docid></filelocked>");
978
				logReplication.info("ReplicationService.handleGetLockRequest - lock denied for " + docid
979
						+ "reason: client has outdated file");
980
			}
981
			out.close();
982
			//conn.close();
983
		} catch (Exception e) {
984
			logMetacat.error("ReplicationService.handleGetLockRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
985
			logReplication.error("ReplicationService.handleGetLockRequest - error requesting file lock from MetacatReplication."
986
					+ "handleGetLockRequest: " + e.getMessage());
987
			e.printStackTrace(System.out);
988
		}
989
	}
990

    
991
	/**
992
	 * Sends all of the xml_documents information encoded in xml to a requestor
993
	 * the format is:
994
	 * <!ELEMENT documentinfo (docid, docname, doctype, doctitle, user_owner,
995
	 *                  user_updated, home_server, public_access, rev)/>
996
	 * all of the subelements of document info are #PCDATA
997
	 */
998
	protected static void handleGetDocumentInfoRequest(
999
			Hashtable<String, String[]> params, HttpServletResponse response) {
1000
		String docid = ((String[]) (params.get("docid")))[0];
1001

    
1002
		try {
1003
			// get docinfo as XML string
1004
			String docinfoXML = getDocumentInfo(docid);
1005
			
1006
			// get a writer for sending back to response
1007
			response.setContentType("text/xml");
1008
			Writer out = response.getWriter();
1009
			out.write(docinfoXML);
1010
			out.close();
1011

    
1012
		} catch (Exception e) {
1013
			logMetacat.error("ReplicationService.handleGetDocumentInfoRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
1014
			logReplication.error("ReplicationService.handleGetDocumentInfoRequest - error in metacatReplication.handlegetdocumentinforequest "
1015
					+ "for doc: " + docid + " : " + e.getMessage());
1016
		}
1017

    
1018
	}
1019
	
1020
	public static Hashtable<String, String> getDocumentInfoMap(String docid)
1021
			throws HandlerException, AccessControlException, JiBXException,
1022
			IOException, McdbException, SAXException {
1023
		
1024
		// Try get docid info from remote server
1025
		DocInfoHandler dih = new DocInfoHandler();
1026
		XMLReader docinfoParser = ReplicationHandler.initParser(dih);
1027

    
1028
		String docInfoStr = getDocumentInfo(docid);
1029

    
1030
		// strip out the system metadata portion
1031
		String systemMetadataXML = ReplicationUtil.getSystemMetadataContent(docInfoStr);
1032
		docInfoStr = ReplicationUtil.getContentWithoutSystemMetadata(docInfoStr);
1033

    
1034
		docinfoParser.parse(new InputSource(new StringReader(docInfoStr)));
1035
		Hashtable<String, String> docinfoHash = dih.getDocInfo();
1036

    
1037
		return docinfoHash;
1038
	}
1039
	
1040
	/**
1041
	 * Gets a docInfo XML snippet for the replication API
1042
	 * @param docid
1043
	 * @return
1044
	 * @throws AccessControlException
1045
	 * @throws JiBXException
1046
	 * @throws IOException
1047
	 * @throws McdbException
1048
	 */
1049
	public static String getDocumentInfo(String docid) throws AccessControlException, JiBXException, IOException, McdbException {
1050
		StringBuffer sb = new StringBuffer();
1051

    
1052
		DocumentImpl doc = new DocumentImpl(docid);
1053
		sb.append("<documentinfo><docid>").append(docid);
1054
		sb.append("</docid>");
1055
		
1056
		try {
1057
			// serialize the System Metadata as XML for docinfo
1058
			String guid = IdentifierManager.getInstance().getGUID(doc.getDocID(), doc.getRev());
1059
			SystemMetadata systemMetadata = IdentifierManager.getInstance().getSystemMetadata(guid);
1060
			ByteArrayOutputStream baos = new ByteArrayOutputStream();
1061
			TypeMarshaller.marshalTypeToOutputStream(systemMetadata, baos);
1062
			String systemMetadataXML = baos.toString("UTF-8");
1063
			sb.append("<systemMetadata>");
1064
			sb.append(systemMetadataXML);
1065
			sb.append("</systemMetadata>");
1066
		} catch(McdbDocNotFoundException e) {
1067
		  logMetacat.warn("No SystemMetadata found for: " + docid);
1068
		}
1069
		
1070
		Calendar created = Calendar.getInstance();
1071
		created.setTime(doc.getCreateDate());
1072
		Calendar updated = Calendar.getInstance();
1073
		updated.setTime(doc.getUpdateDate());
1074
		
1075
		sb.append("<docname><![CDATA[").append(doc.getDocname());
1076
		sb.append("]]></docname><doctype>").append(doc.getDoctype());
1077
		sb.append("</doctype>");
1078
		sb.append("<user_owner>").append(doc.getUserowner());
1079
		sb.append("</user_owner><user_updated>").append(doc.getUserupdated());
1080
		sb.append("</user_updated>");
1081
		sb.append("<date_created>");
1082
		sb.append(DateTimeMarshaller.serializeDateToUTC(doc.getCreateDate()));
1083
		sb.append("</date_created>");
1084
		sb.append("<date_updated>");
1085
		sb.append(DateTimeMarshaller.serializeDateToUTC(doc.getUpdateDate()));
1086
		sb.append("</date_updated>");
1087
		sb.append("<home_server>");
1088
		sb.append(doc.getDocHomeServer());
1089
		sb.append("</home_server>");
1090
		sb.append("<public_access>").append(doc.getPublicaccess());
1091
		sb.append("</public_access><rev>").append(doc.getRev());
1092
		sb.append("</rev>");
1093

    
1094
		sb.append("<accessControl>");
1095

    
1096
		AccessControlForSingleFile acfsf = new AccessControlForSingleFile(docid); 
1097
		sb.append(acfsf.getAccessString());
1098
		
1099
		sb.append("</accessControl>");
1100

    
1101
		sb.append("</documentinfo>");
1102
			
1103
		return sb.toString();
1104
	}
1105
	
1106
	/**
1107
	 * Sends System Metadata as XML
1108
	 */
1109
	protected static void handleGetSystemMetadataRequest(
1110
			Hashtable<String, String[]> params, HttpServletResponse response) {
1111
		String guid = ((String[]) (params.get("guid")))[0];
1112
		String systemMetadataXML = null;
1113
		try {
1114
			
1115
			// serialize the System Metadata as XML 
1116
			SystemMetadata systemMetadata = IdentifierManager.getInstance().getSystemMetadata(guid);
1117
			ByteArrayOutputStream baos = new ByteArrayOutputStream();
1118
			TypeMarshaller.marshalTypeToOutputStream(systemMetadata, baos);
1119
			systemMetadataXML = baos.toString("UTF-8");
1120
				
1121
			// get a writer for sending back to response
1122
			response.setContentType("text/xml");
1123
			Writer out = response.getWriter();
1124
			out.write(systemMetadataXML);
1125
			out.close();
1126

    
1127
		} catch (Exception e) {
1128
			String msg = "ReplicationService.handleGetSystemMetadataRequest for guid: " + guid + " : " + e.getMessage();
1129
			logMetacat.error(msg);                         
1130
			logReplication.error(msg);
1131
		}
1132

    
1133
	}
1134
	
1135
	/**
1136
	 * when a forcereplication request comes in, local host sends a read request
1137
	 * to the requesting server (remote server) for the specified docid. Then
1138
	 * store it in local database.
1139
	 */
1140
	protected static void handleForceReplicateSystemMetadataRequest(
1141
			Hashtable<String, String[]> params, HttpServletResponse response,
1142
			HttpServletRequest request) {
1143
		String server = ((String[]) params.get("server"))[0]; // the server that
1144
		String guid = ((String[]) params.get("guid"))[0]; // sent the document
1145
		
1146
		try {
1147
			logReplication.info("ReplicationService.handleForceReplicateSystemMetadataRequest - Force replication system metadata request from: " + server);
1148
			// get the system metadata from server
1149
			URL docinfourl = new URL("https://" + server + "?server="
1150
					+ MetacatUtil.getLocalReplicationServerName()
1151
					+ "&action=getsystemmetadata&guid=" + guid);
1152
			
1153
			String systemMetadataXML = ReplicationService.getURLContent(docinfourl);
1154
						
1155
			// process system metadata
1156
			if (systemMetadataXML != null) {
1157
				SystemMetadata sysMeta = 
1158
					TypeMarshaller.unmarshalTypeFromStream(
1159
							SystemMetadata.class,
1160
							new ByteArrayInputStream(systemMetadataXML.getBytes("UTF-8")));
1161
				HazelcastService.getInstance().getSystemMetadataMap().put(sysMeta.getIdentifier(), sysMeta);
1162
				// submit for indexing
1163
                HazelcastService.getInstance().getIndexQueue().add(sysMeta);
1164
			}
1165
      
1166
			logReplication.info("ReplicationService.handleForceReplicateSystemMetadataRequest - processed guid: " + guid);
1167
			EventLog.getInstance().log(request.getRemoteAddr(), request.getHeader("User-Agent"), REPLICATIONUSER, guid, "systemMetadata");
1168

    
1169
		} catch (Exception e) {
1170
			logMetacat.error("ReplicationService.handleForceReplicateRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG, e);                         
1171
			logReplication.error("ReplicationService.handleForceReplicateRequest - General error when processing guid: " + guid, e);
1172
		}
1173
	}
1174

    
1175
	/**
1176
	 * Sends a datafile to a remote host
1177
	 */
1178
	protected static void handleGetDataFileRequest(OutputStream outPut,
1179
			Hashtable<String, String[]> params, HttpServletResponse response)
1180

    
1181
	{
1182
		// File path for data file
1183
		String filepath;
1184
		// Request docid
1185
		String docId = ((String[]) (params.get("docid")))[0];
1186
		//check if the doicd is null
1187
		if (docId == null) {
1188
			logMetacat.error("ReplicationService.handleGetDataFileRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
1189
			logReplication.error("ReplicationService.handleGetDataFileRequest - Didn't specify docid for replication");
1190
			return;
1191
		}
1192

    
1193
		//try to open a https stream to test if the request server's public key
1194
		//in the key store, this is security issue
1195
		try {
1196
			filepath = PropertyService.getProperty("application.datafilepath");
1197
			String server = params.get("server")[0];
1198
			URL u = new URL("https://" + server + "?server="
1199
					+ MetacatUtil.getLocalReplicationServerName() + "&action=test");
1200
			String test = ReplicationService.getURLContent(u);
1201
			//couldn't pass the test
1202
			if (test.indexOf("successfully") == -1) {
1203
				//response.setContentType("text/xml");
1204
				//outPut.println("<error>Couldn't pass the trust test</error>");
1205
				logMetacat.error("ReplicationService.handleGetDataFileRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
1206
				logReplication.error("ReplicationService.handleGetDataFileRequest - Couldn't pass the trust test");
1207
				return;
1208
			}
1209
		}//try
1210
		catch (Exception ee) {
1211
			return;
1212
		}//catch
1213

    
1214
		if (!filepath.endsWith("/")) {
1215
			filepath += "/";
1216
		}
1217
		// Get file aboslute file name
1218
		String filename = filepath + docId;
1219

    
1220
		//MIME type
1221
		String contentType = null;
1222
		if (filename.endsWith(".xml")) {
1223
			contentType = "text/xml";
1224
		} else if (filename.endsWith(".css")) {
1225
			contentType = "text/css";
1226
		} else if (filename.endsWith(".dtd")) {
1227
			contentType = "text/plain";
1228
		} else if (filename.endsWith(".xsd")) {
1229
			contentType = "text/xml";
1230
		} else if (filename.endsWith("/")) {
1231
			contentType = "text/html";
1232
		} else {
1233
			File f = new File(filename);
1234
			if (f.isDirectory()) {
1235
				contentType = "text/html";
1236
			} else {
1237
				contentType = "application/octet-stream";
1238
			}
1239
		}
1240

    
1241
		// Set the mime type
1242
		response.setContentType(contentType);
1243

    
1244
		// Get the content of the file
1245
		FileInputStream fin = null;
1246
		try {
1247
			// FileInputStream to metacat
1248
			fin = new FileInputStream(filename);
1249
			// 4K buffer
1250
			byte[] buf = new byte[4 * 1024];
1251
			// Read data from file input stream to byte array
1252
			int b = fin.read(buf);
1253
			// Write to outStream from byte array
1254
			while (b != -1) {
1255
				outPut.write(buf, 0, b);
1256
				b = fin.read(buf);
1257
			}
1258
			// close file input stream
1259
			fin.close();
1260

    
1261
		} catch (Exception e) {
1262
			logMetacat.error("ReplicationService.handleGetDataFileRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
1263
			logReplication.error("ReplicationService.handleGetDataFileRequest - error getting data file from MetacatReplication."
1264
					+ "handlGetDataFileRequest " + e.getMessage());
1265
			e.printStackTrace(System.out);
1266
		} finally {
1267
		    IOUtils.closeQuietly(fin);
1268
		}
1269

    
1270
	}
1271

    
1272
	/**
1273
	 * Sends a document to a remote host
1274
	 */
1275
	protected static void handleGetDocumentRequest(
1276
			Hashtable<String, String[]> params, HttpServletResponse response) {
1277

    
1278
		String urlString = null;
1279
		String documentPath = null;
1280
		String errorMsg = null;
1281
		FileOutputStream fos = null;
1282
		InputStream is = null;
1283
		OutputStream outputStream = null;
1284
		try {
1285
			// try to open a https stream to test if the request server's public
1286
			// key
1287
			// in the key store, this is security issue
1288
			String server = params.get("server")[0];
1289
			urlString = "https://" + server + "?server="
1290
					+ MetacatUtil.getLocalReplicationServerName() + "&action=test";
1291
			URL u = new URL(urlString);
1292
			String test = ReplicationService.getURLContent(u);
1293
			// couldn't pass the test
1294
			if (test.indexOf("successfully") == -1) {
1295
				response.setContentType("text/xml");
1296
				Writer out = response.getWriter();
1297
				out.write("<error>Couldn't pass the trust test " + test + " </error>");
1298
				out.close();
1299
				return;
1300
			}
1301

    
1302
			String docid = params.get("docid")[0];
1303
			logReplication.debug("ReplicationService.handleGetDocumentRequest - MetacatReplication.handleGetDocumentRequest for docid: "
1304
					+ docid);
1305
			DocumentImpl di = new DocumentImpl(docid);
1306

    
1307
			String documentDir = PropertyService
1308
					.getProperty("application.documentfilepath");
1309
			documentPath = documentDir + FileUtil.getFS() + docid;
1310

    
1311
			// if the document does not exist on disk, read it from db and write
1312
			// it to disk.
1313
			if (FileUtil.getFileStatus(documentPath) == FileUtil.DOES_NOT_EXIST
1314
					|| FileUtil.getFileSize(documentPath) == 0) {
1315
				fos = new FileOutputStream(documentPath);
1316
				is = di.toXml(fos, null, null, true);
1317
				fos.close();
1318
				is.close();
1319
			}
1320

    
1321
			// read the file from disk and send it to outputstream
1322
			outputStream = response.getOutputStream();
1323
			is = di.readFromFileSystem(outputStream, null, null, documentPath);
1324
			is.close();
1325
			outputStream.close();
1326

    
1327
			logReplication.info("ReplicationService.handleGetDocumentRequest - document " + docid + " sent");
1328

    
1329
			// return to avoid continuing to the error reporting section at the end
1330
			return;
1331
			
1332
		} catch (MalformedURLException mue) {
1333
			logMetacat.error("ReplicationService.handleGetDocumentRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
1334
			logReplication.error("ReplicationService.handleGetDocumentRequest - Url error when getting document from MetacatReplication."
1335
					+ "handlGetDocumentRequest for url: " + urlString + " : "
1336
					+ mue.getMessage());
1337
			// e.printStackTrace(System.out);
1338
			
1339
		} catch (IOException ioe) {
1340
			logMetacat.error("ReplicationService.handleGetDocumentRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
1341
			logReplication.error("ReplicationService.handleGetDocumentRequest - I/O error when getting document from MetacatReplication."
1342
					+ "handlGetDocumentRequest for file: " + documentPath + " : "
1343
					+ ioe.getMessage());
1344
			errorMsg = ioe.getMessage();
1345
		} catch (PropertyNotFoundException pnfe) {
1346
			logMetacat.error("ReplicationService.handleGetDocumentRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
1347
			logReplication
1348
					.error("ReplicationService.handleGetDocumentRequest - Error getting property when getting document from MetacatReplication."
1349
							+ "handlGetDocumentRequest for file: "
1350
							+ documentPath
1351
							+ " : "
1352
							+ pnfe.getMessage());
1353
			// e.printStackTrace(System.out);
1354
			errorMsg = pnfe.getMessage();
1355
		} catch (McdbException me) {
1356
			logReplication
1357
					.error("ReplicationService.handleGetDocumentRequest - Document implementation error  getting property when getting document from MetacatReplication."
1358
							+ "handlGetDocumentRequest for file: "
1359
							+ documentPath
1360
							+ " : "
1361
							+ me.getMessage());
1362
			// e.printStackTrace(System.out);
1363
			errorMsg = me.getMessage();
1364
		} finally {
1365
            IOUtils.closeQuietly(fos);
1366
            IOUtils.closeQuietly(is);
1367
            IOUtils.closeQuietly(outputStream);
1368
		}
1369
		
1370
		// report any errors if we got here
1371
		response.setContentType("text/xml");
1372
		Writer out = null;
1373
		try {
1374
			response.getWriter();
1375
			out = response.getWriter();
1376
			out.write("<error>" + errorMsg + "</error>");
1377
		} catch (Exception e) {
1378
			logMetacat.error(e.getMessage(), e);
1379
		} finally {
1380
			try {
1381
				out.close();
1382
			} catch (IOException e) {
1383
				logMetacat.error(e.getMessage(), e);
1384
			}
1385
		}
1386
		
1387

    
1388
	}
1389

    
1390
	/**
1391
	 * Sends a list of all of the documents on this sever along with their
1392
	 * revision numbers. The format is: <!ELEMENT replication (server, updates)>
1393
	 * <!ELEMENT server (#PCDATA)> <!ELEMENT updates ((updatedDocument |
1394
	 * deleteDocument | revisionDocument)*)> <!ELEMENT updatedDocument (docid,
1395
	 * rev, datafile*)> <!ELEMENT deletedDocument (docid, rev)> <!ELEMENT
1396
	 * revisionDocument (docid, rev, datafile*)> <!ELEMENT docid (#PCDATA)>
1397
	 * <!ELEMENT rev (#PCDATA)> <!ELEMENT datafile (#PCDATA)> note that the rev
1398
	 * in deletedDocument is always empty. I just left it in there to make the
1399
	 * parser implementation easier.
1400
	 */
1401
	protected static void handleUpdateRequest(Hashtable<String, String[]> params,
1402
			HttpServletResponse response) {
1403
		// Checked out DBConnection
1404
		DBConnection dbConn = null;
1405
		// DBConenction serial number when checked it out
1406
		int serialNumber = -1;
1407
		PreparedStatement pstmt = null;
1408
		// Server list to store server info of xml_replication table
1409
		ReplicationServerList serverList = null;
1410
		
1411
		// a writer for response
1412
		Writer out = null;
1413

    
1414
		try {
1415
			// get writer, TODO: encoding?
1416
			response.setContentType("text/xml");
1417
			out = response.getWriter();
1418
			
1419
			// Check out a DBConnection from pool
1420
			dbConn = DBConnectionPool
1421
					.getDBConnection("MetacatReplication.handleUpdateRequest");
1422
			serialNumber = dbConn.getCheckOutSerialNumber();
1423
			// Create a server list from xml_replication table
1424
			serverList = new ReplicationServerList();
1425

    
1426
			// Get remote server name from param
1427
			String server = ((String[]) params.get("server"))[0];
1428
			// If no servr name in param, return a error
1429
			if (server == null || server.equals("")) {
1430
				out.write("<error>Request didn't specify server name</error>");
1431
				out.close();
1432
				return;
1433
			}//if
1434

    
1435
			//try to open a https stream to test if the request server's public key
1436
			//in the key store, this is security issue
1437
			String testUrl = "https://" + server + "?server="
1438
            + MetacatUtil.getLocalReplicationServerName() + "&action=test";
1439
			logReplication.info("Running trust test: " + testUrl);
1440
			URL u = new URL(testUrl);
1441
			String test = ReplicationService.getURLContent(u);
1442
			logReplication.info("Ouput from test is '" + test + "'");
1443
			//couldn't pass the test
1444
			if (test.indexOf("successfully") == -1) {
1445
			    logReplication.error("Trust test failed.");
1446
				out.write("<error>Couldn't pass the trust test</error>");
1447
				out.close();
1448
				return;
1449
			}
1450
			logReplication.info("Trust test succeeded.");
1451

    
1452
			// Check if local host configure to replicate xml documents to remote
1453
			// server. If not send back a error message
1454
			if (!serverList.getReplicationValue(server)) {
1455
				out.write("<error>Configuration not allow to replicate document to you</error>");
1456
				out.close();
1457
				return;
1458
			}//if
1459

    
1460
			// Store the sql command
1461
			StringBuffer docsql = new StringBuffer();
1462
			StringBuffer revisionSql = new StringBuffer();
1463
			
1464
			// Store the data set file
1465
			Vector<Vector<String>> packageFiles = new Vector<Vector<String>>();
1466

    
1467
			// Append local server's name and replication servlet to doclist
1468
			out.append("<?xml version=\"1.0\"?><replication>");
1469
			out.append("<server>")
1470
					.append(MetacatUtil.getLocalReplicationServerName());
1471
			//doclist.append(util.getProperty("replicationpath"));
1472
			out.append("</server><updates>");
1473

    
1474
			// Get correct docid that reside on this server according the requesting
1475
			// server's replicate and data replicate value in xml_replication table
1476
			docsql.append(DatabaseService.getInstance().getDBAdapter().getReplicationDocumentListSQL());
1477
			//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)) ");
1478
			revisionSql.append("select docid, rev, doctype from xml_revisions ");
1479
			// If the localhost is not a hub to the remote server, only replicate
1480
			// the docid' which home server is local host (server_location =1)
1481
			if (!serverList.getHubValue(server)) {
1482
				String serverLocationDoc = " and a.server_location = 1";
1483
				String serverLocationRev = "where server_location = 1";
1484
				docsql.append(serverLocationDoc);
1485
				revisionSql.append(serverLocationRev);
1486
			}
1487
			logReplication.info("ReplicationService.handleUpdateRequest - Doc sql: " + docsql.toString());
1488

    
1489
			// Get any deleted documents
1490
			StringBuffer delsql = new StringBuffer();
1491
			delsql.append("SELECT t1.docid FROM xml_revisions t1 LEFT JOIN xml_documents t2 on t1.docid = t2.docid WHERE t2.docid IS NULL "); 
1492
			
1493
			// If the localhost is not a hub to the remote server, only replicate
1494
			// the docid' which home server is local host (server_location =1)
1495
			if (!serverList.getHubValue(server)) {
1496
				delsql.append("and t1.server_location = 1");
1497
			}
1498
			logReplication.info("ReplicationService.handleUpdateRequest - Deleted sql: " + delsql.toString());
1499

    
1500
			// Get docid list of local host
1501
			pstmt = dbConn.prepareStatement(docsql.toString());
1502
			pstmt.execute();
1503
			ResultSet rs = pstmt.getResultSet();
1504
			boolean tablehasrows = rs.next();
1505
			//If metacat configed to replicate data file
1506
			//if ((util.getProperty("replicationsenddata")).equals("on"))
1507
			boolean replicateData = serverList.getDataReplicationValue(server);
1508
			if (replicateData) {
1509
				while (tablehasrows) {
1510
					String recordDoctype = rs.getString(3);
1511
					Vector<String> packagedoctypes = MetacatUtil
1512
							.getOptionList(PropertyService
1513
									.getProperty("xml.packagedoctype"));
1514
					//if this is a package file, put it at the end
1515
					//because if a package file is read before all of the files it
1516
					//refers to are loaded then there is an error
1517
					if (recordDoctype != null && !packagedoctypes.contains(recordDoctype)) {
1518
						//If this is not data file
1519
						if (!recordDoctype.equals("BIN")) {
1520
							//for non-data file document
1521
							out.append("<updatedDocument>");
1522
							out.append("<docid>").append(rs.getString(1));
1523
							out.append("</docid><rev>" + rs.getInt(2));
1524
							out.append("</rev>");
1525
							out.append("</updatedDocument>");
1526
						}//if
1527
						else {
1528
							//for data file document, in datafile attributes
1529
							//we put "datafile" value there
1530
							out.append("<updatedDocument>");
1531
							out.append("<docid>").append(rs.getString(1));
1532
							out.append("</docid><rev>" + rs.getInt(2));
1533
							out.append("</rev>");
1534
							out.append("<datafile>");
1535
							out.append(DATA_FILE_FLAG);
1536
							out.append("</datafile>");
1537
							out.append("</updatedDocument>");
1538
						}//else
1539
					}//if packagedoctpes
1540
					else { //the package files are saved to be put into the xml later.
1541
						Vector<String> v = new Vector<String>();
1542
						v.add(rs.getString(1));
1543
						v.add(String.valueOf(rs.getInt(2)));
1544
						packageFiles.add(v);
1545
					}//esle
1546
					tablehasrows = rs.next();
1547
				}//while
1548
			}//if
1549
			else //metacat was configured not to send data file
1550
			{
1551
				while (tablehasrows) {
1552
					String recordDoctype = rs.getString(3);
1553
					if (!recordDoctype.equals("BIN")) { //don't replicate data files
1554
						Vector<String> packagedoctypes = MetacatUtil
1555
								.getOptionList(PropertyService
1556
										.getProperty("xml.packagedoctype"));
1557
						if (recordDoctype != null
1558
								&& !packagedoctypes.contains(recordDoctype)) { //if this is a package file, put it at the end
1559
							//because if a package file is read before all of the files it
1560
							//refers to are loaded then there is an error
1561
							out.append("<updatedDocument>");
1562
							out.append("<docid>" + rs.getString(1));
1563
							out.append("</docid><rev>" + rs.getInt(2));
1564
							out.append("</rev>");
1565
							out.append("</updatedDocument>");
1566
						} else { //the package files are saved to be put into the xml later.
1567
							Vector<String> v = new Vector<String>();
1568
							v.add(rs.getString(1));
1569
							v.add(String.valueOf(rs.getInt(2)));
1570
							packageFiles.add(v);
1571
						}
1572
					}//if
1573
					tablehasrows = rs.next();
1574
				}//while
1575
			}//else
1576

    
1577
			pstmt = dbConn.prepareStatement(delsql.toString());
1578
			//usage count should increas 1
1579
			dbConn.increaseUsageCount(1);
1580

    
1581
			pstmt.execute();
1582
			rs = pstmt.getResultSet();
1583
			tablehasrows = rs.next();
1584
			while (tablehasrows) { //handle the deleted documents
1585
				out.append("<deletedDocument><docid>").append(rs.getString(1));
1586
				out.append("</docid><rev></rev></deletedDocument>");
1587
				//note that rev is always empty for deleted docs
1588
				tablehasrows = rs.next();
1589
			}
1590

    
1591
			//now we can put the package files into the xml results
1592
			for (int i = 0; i < packageFiles.size(); i++) {
1593
				Vector<String> v = packageFiles.elementAt(i);
1594
				out.append("<updatedDocument>");
1595
				out.append("<docid>").append(v.elementAt(0));
1596
				out.append("</docid><rev>");
1597
				out.append(v.elementAt(1));
1598
				out.append("</rev>");
1599
				out.append("</updatedDocument>");
1600
			}
1601
			// add revision doc list  
1602
			out.append(prepareRevisionDoc(dbConn, revisionSql.toString(),
1603
					replicateData));
1604

    
1605
			out.append("</updates></replication>");
1606
			logReplication.info("ReplicationService.handleUpdateRequest - done writing to output stream.");
1607
			pstmt.close();
1608
			//conn.close();
1609

    
1610
		} catch (Exception e) {
1611
			logMetacat.error("ReplicationService.handleUpdateRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
1612
			logReplication.error("ReplicationService.handleUpdateRequest - error in MetacatReplication." + "handleupdaterequest: "
1613
					+ e.getMessage());
1614
			//e.printStackTrace(System.out);
1615
			try {
1616
				out.write("<error>" + e.getMessage() + "</error>");
1617
			} catch (IOException e1) {
1618
				logMetacat.error(e1.getMessage(), e1);
1619
			}
1620
		} finally {
1621
			try {
1622
				pstmt.close();
1623
			}//try
1624
			catch (SQLException ee) {
1625
				logMetacat.error("ReplicationService.handleUpdateRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
1626
				logReplication.error("ReplicationService.handleUpdateRequest - Error in MetacatReplication."
1627
						+ "handleUpdaterequest to close pstmt: " + ee.getMessage());
1628
			}//catch
1629
			finally {
1630
				DBConnectionPool.returnDBConnection(dbConn, serialNumber);
1631
			}//finally
1632
			try {
1633
				out.close();
1634
			} catch (IOException e) {
1635
				logMetacat.error(e.getMessage(), e);
1636
			}
1637
		}//finally
1638

    
1639
	}//handlUpdateRequest
1640

    
1641
	/**
1642
	 * 
1643
	 * @param dbConn connection for doing the update
1644
	 * @param docid the document id to update
1645
	 * @param owner the user_owner
1646
	 * @param updater the user_updated
1647
	 * @throws SQLException
1648
	 */
1649
	public static void updateUserOwner(DBConnection dbConn, String docid, String owner, String updater) throws SQLException {
1650
	
1651
		String sql = 
1652
			"UPDATE xml_documents " +
1653
			"SET user_owner = ?, " +
1654
			"user_updated = ? " +
1655
			"WHERE docid = ?;";
1656
		PreparedStatement pstmt = dbConn.prepareStatement(sql);
1657
		//usage count should increas 1
1658
		dbConn.increaseUsageCount(1);
1659

    
1660
		docid = DocumentUtil.getSmartDocId(docid);
1661
		pstmt.setString(1, owner);
1662
		pstmt.setString(2, updater);
1663
		pstmt.setString(3, docid);
1664
		pstmt.execute();
1665
		pstmt.close();
1666
		
1667
		dbConn.commit();
1668
	}
1669
	
1670
	/*
1671
	 * This method will get the xml string for document in xml_revision
1672
	 * The schema look like <!ELEMENT revisionDocument (docid, rev, datafile*)>
1673
	 */
1674
	private static String prepareRevisionDoc(DBConnection dbConn, String revSql,
1675
			boolean replicateData) throws Exception {
1676
		logReplication.warn("ReplicationService.prepareRevisionDoc - The revision document sql is " + revSql);
1677
		StringBuffer revDocList = new StringBuffer();
1678
		PreparedStatement pstmt = dbConn.prepareStatement(revSql);
1679
		//usage count should increas 1
1680
		dbConn.increaseUsageCount(1);
1681

    
1682
		pstmt.execute();
1683
		ResultSet rs = pstmt.getResultSet();
1684
		logReplication.warn("Processing replication revision for documents");
1685
		while (rs.next()) {
1686
			String recordDoctype = rs.getString(3);
1687

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

    
1692
				// do nothing
1693
			} else {
1694
				String docid = rs.getString(1);
1695
				int rev = rs.getInt(2);
1696
				logMetacat.debug("Processing replication revision for docid: " + docid + "." + rev);
1697

    
1698
				revDocList.append("<revisionDocument>");
1699
				revDocList.append("<docid>").append(docid);
1700
				revDocList.append("</docid><rev>").append(rev);
1701
				revDocList.append("</rev>");
1702
				// data file
1703
				if (recordDoctype.equals("BIN")) {
1704
					revDocList.append("<datafile>");
1705
					revDocList.append(DATA_FILE_FLAG);
1706
					revDocList.append("</datafile>");
1707
				}
1708
				revDocList.append("</revisionDocument>");
1709

    
1710
			}//else
1711
		}
1712
		//System.out.println("The revision list is"+ revDocList.toString());
1713
		return revDocList.toString();
1714
	}
1715

    
1716
	/**
1717
	 * Returns the xml_catalog table encoded in xml
1718
	 */
1719
	public static String getCatalogXML() {
1720
		return handleGetCatalogRequest(null, null, false);
1721
	}
1722

    
1723
	/**
1724
	 * Sends the contents of the xml_catalog table encoded in xml
1725
	 * The xml format is:
1726
	 * <!ELEMENT xml_catalog (row*)>
1727
	 * <!ELEMENT row (entry_type, source_doctype, target_doctype, public_id,
1728
	 *                system_id)>
1729
	 * All of the sub elements of row are #PCDATA
1730

    
1731
	 * If printFlag == false then do not print to out.
1732
	 */
1733
	protected static String handleGetCatalogRequest(
1734
			Hashtable<String, String[]> params, HttpServletResponse response,
1735
			boolean printFlag) {
1736
		DBConnection dbConn = null;
1737
		int serialNumber = -1;
1738
		PreparedStatement pstmt = null;
1739
		Writer out = null;
1740
		try {
1741
			// get writer, TODO: encoding?
1742
		    if(printFlag)
1743
		    {
1744
		        response.setContentType("text/xml");
1745
		        out = response.getWriter();
1746
		    }
1747
			/*conn = MetacatReplication.getDBConnection("MetacatReplication." +
1748
			                                          "handleGetCatalogRequest");*/
1749
			dbConn = DBConnectionPool
1750
					.getDBConnection("MetacatReplication.handleGetCatalogRequest");
1751
			serialNumber = dbConn.getCheckOutSerialNumber();
1752
			pstmt = dbConn.prepareStatement("select entry_type, "
1753
					+ "source_doctype, target_doctype, public_id, "
1754
					+ "system_id from xml_catalog");
1755
			pstmt.execute();
1756
			ResultSet rs = pstmt.getResultSet();
1757
			boolean tablehasrows = rs.next();
1758
			StringBuffer sb = new StringBuffer();
1759
			sb.append("<?xml version=\"1.0\"?><xml_catalog>");
1760
			while (tablehasrows) {
1761
				sb.append("<row><entry_type>").append(rs.getString(1));
1762
				sb.append("</entry_type><source_doctype>").append(rs.getString(2));
1763
				sb.append("</source_doctype><target_doctype>").append(rs.getString(3));
1764
				sb.append("</target_doctype><public_id>").append(rs.getString(4));
1765
				// system id may not have server url on front.  Add it if not.
1766
				String systemID = rs.getString(5);
1767
				if (!systemID.startsWith("http://")) {
1768
					systemID = SystemUtil.getContextURL() + systemID;
1769
				}
1770
				sb.append("</public_id><system_id>").append(systemID);
1771
				sb.append("</system_id></row>");
1772

    
1773
				tablehasrows = rs.next();
1774
			}
1775
			sb.append("</xml_catalog>");
1776
			//conn.close();
1777
			if (printFlag) {
1778
				response.setContentType("text/xml");
1779
				out.write(sb.toString());
1780
			}
1781
			pstmt.close();
1782
			return sb.toString();
1783
		} catch (Exception e) {
1784
			logMetacat.error("ReplicationService.handleGetCatalogRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
1785
			logReplication.error("ReplicationService.handleGetCatalogRequest - error in MetacatReplication.handleGetCatalogRequest:"
1786
					+ e.getMessage());
1787
			e.printStackTrace(System.out);
1788
			if (printFlag) {
1789
				try {
1790
					out.write("<error>" + e.getMessage() + "</error>");
1791
				} catch (IOException e1) {
1792
					logMetacat.error(e1.getMessage(), e1);
1793
				}
1794
			}
1795
		} finally {
1796
			try {
1797
				pstmt.close();
1798
			}//try
1799
			catch (SQLException ee) {
1800
				logMetacat.error("ReplicationService.handleGetCatalogRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
1801
				logReplication.error("ReplicationService.handleGetCatalogRequest - Error in MetacatReplication.handleGetCatalogRequest: "
1802
						+ ee.getMessage());
1803
			}//catch
1804
			finally {
1805
				DBConnectionPool.returnDBConnection(dbConn, serialNumber);
1806
			}//finally
1807
			if (out != null) {
1808
				try {
1809
					out.close();
1810
				} catch (IOException e1) {
1811
					logMetacat.error(e1.getMessage(), e1);
1812
				}
1813
			}
1814
		}//finally
1815

    
1816
		return null;
1817
	}
1818

    
1819
	/**
1820
	 * Sends the current system date to the remote server.  Using this action
1821
	 * for replication gets rid of any problems with syncronizing clocks
1822
	 * because a time specific to a document is always kept on its home server.
1823
	 */
1824
	protected static void handleGetTimeRequest(
1825
			Hashtable<String, String[]> params, HttpServletResponse response) {
1826
		
1827
		// use standard format -- the receiving end wants this too
1828
		String dateString = DateTimeMarshaller.serializeDateToUTC(Calendar.getInstance().getTime());
1829
		
1830
		// get a writer for sending back to response
1831
		response.setContentType("text/xml");
1832
		Writer out = null;
1833
		try {
1834
			out = response.getWriter();
1835
			out.write("<timestamp>" + dateString + "</timestamp>");
1836
			out.close();
1837
		} catch (IOException e) {
1838
			logMetacat.error(e.getMessage(), e);
1839
		}
1840
		
1841
	}
1842

    
1843
	/**
1844
	 * this method handles the timeout for a file lock.  when a lock is
1845
	 * granted it is granted for 30 seconds.  When this thread runs out
1846
	 * it deletes the docid from the queue, thus eliminating the lock.
1847
	 */
1848
	public void run() {
1849
		try {
1850
			logReplication.info("ReplicationService.run - thread started for docid: "
1851
					+ (String) fileLocks.elementAt(0));
1852

    
1853
			Thread.sleep(30000); //the lock will expire in 30 seconds
1854
			logReplication.info("thread for docid: "
1855
					+ (String) fileLocks.elementAt(fileLocks.size() - 1) + " exiting.");
1856

    
1857
			fileLocks.remove(fileLocks.size() - 1);
1858
			//fileLocks is treated as a FIFO queue.  If there are more than one lock
1859
			//in the vector, the first one inserted will be removed.
1860
		} catch (Exception e) {
1861
			logMetacat.error("ReplicationService.run - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
1862
			logReplication.error("ReplicationService.run - error in file lock thread from "
1863
					+ "MetacatReplication.run: " + e.getMessage());
1864
		}
1865
	}
1866

    
1867
	/**
1868
	 * Returns the name of a server given a serverCode
1869
	 * @param serverCode the serverid of the server
1870
	 * @return the servername or null if the specified serverCode does not
1871
	 *         exist.
1872
	 */
1873
	public static String getServerNameForServerCode(int serverCode) {
1874
		//System.out.println("serverid: " + serverCode);
1875
		DBConnection dbConn = null;
1876
		int serialNumber = -1;
1877
		PreparedStatement pstmt = null;
1878
		try {
1879
			dbConn = DBConnectionPool.getDBConnection("MetacatReplication.getServer");
1880
			serialNumber = dbConn.getCheckOutSerialNumber();
1881
			String sql = new String("select server from "
1882
					+ "xml_replication where serverid = ?");
1883
			pstmt = dbConn.prepareStatement(sql);
1884
			pstmt.setInt(1, serverCode);
1885
			//System.out.println("getserver sql: " + sql);
1886
			pstmt.execute();
1887
			ResultSet rs = pstmt.getResultSet();
1888
			boolean tablehasrows = rs.next();
1889
			if (tablehasrows) {
1890
				//System.out.println("server: " + rs.getString(1));
1891
				return rs.getString(1);
1892
			}
1893

    
1894
			//conn.close();
1895
		} catch (Exception e) {
1896
			logMetacat.error("ReplicationService.getServerNameForServerCode - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
1897
			logReplication.error("ReplicationService.getServerNameForServerCode - Error in MetacatReplication.getServer: " + e.getMessage());
1898
		} finally {
1899
			try {
1900
				pstmt.close();
1901
			}//try
1902
			catch (SQLException ee) {
1903
				logMetacat.error("ReplicationService.getServerNameForServerCode - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
1904
				logReplication.error("ReplicationService.getServerNameForServerCode - Error in MetacactReplication.getserver: "
1905
						+ ee.getMessage());
1906
			}//catch
1907
			finally {
1908
				DBConnectionPool.returnDBConnection(dbConn, serialNumber);
1909
			}//fianlly
1910
		}//finally
1911

    
1912
		return null;
1913
		//return null if the server does not exist
1914
	}
1915

    
1916
	/**
1917
	 * Returns a server code given a server name
1918
	 * @param server the name of the server
1919
	 * @return integer > 0 representing the code of the server, 0 if the server
1920
	 *  does not exist.
1921
	 */
1922
	public static int getServerCodeForServerName(String server) throws ServiceException {
1923
		DBConnection dbConn = null;
1924
		int serialNumber = -1;
1925
		PreparedStatement pstmt = null;
1926
		int serverCode = 0;
1927

    
1928
		try {
1929

    
1930
			//conn = util.openDBConnection();
1931
			dbConn = DBConnectionPool.getDBConnection("MetacatReplication.getServerCode");
1932
			serialNumber = dbConn.getCheckOutSerialNumber();
1933
			pstmt = dbConn.prepareStatement("SELECT serverid FROM xml_replication "
1934
					+ "WHERE server LIKE ?");
1935
			pstmt.setString(1, server);
1936
			pstmt.execute();
1937
			ResultSet rs = pstmt.getResultSet();
1938
			boolean tablehasrows = rs.next();
1939
			if (tablehasrows) {
1940
				serverCode = rs.getInt(1);
1941
				pstmt.close();
1942
				//conn.close();
1943
				return serverCode;
1944
			}
1945

    
1946
		} catch (SQLException sqle) {
1947
			throw new ServiceException("ReplicationService.getServerCodeForServerName - " 
1948
					+ "SQL error when getting server code: " + sqle.getMessage());
1949

    
1950
		} finally {
1951
			try {
1952
				pstmt.close();
1953
				//conn.close();
1954
			}//try
1955
			catch (Exception ee) {
1956
				logMetacat.error("ReplicationService.getServerCodeForServerName - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
1957
				logReplication.error("ReplicationService.getServerNameForServerCode - Error in MetacatReplicatio.getServerCode: "
1958
						+ ee.getMessage());
1959

    
1960
			}//catch
1961
			finally {
1962
				DBConnectionPool.returnDBConnection(dbConn, serialNumber);
1963
			}//finally
1964
		}//finally
1965

    
1966
		return serverCode;
1967
	}
1968

    
1969
	/**
1970
	 * Method to get a host server information for given docid
1971
	 * @param conn a connection to the database
1972
	 */
1973
	public static Hashtable<String, String> getHomeServerInfoForDocId(String docId) {
1974
		Hashtable<String, String> sl = new Hashtable<String, String>();
1975
		DBConnection dbConn = null;
1976
		int serialNumber = -1;
1977
		docId = DocumentUtil.getDocIdFromString(docId);
1978
		PreparedStatement pstmt = null;
1979
		int serverLocation;
1980
		try {
1981
			//get conection
1982
			dbConn = DBConnectionPool.getDBConnection("ReplicationHandler.getHomeServer");
1983
			serialNumber = dbConn.getCheckOutSerialNumber();
1984
			//get a server location from xml_document table
1985
			pstmt = dbConn.prepareStatement("select server_location from xml_documents "
1986
					+ "where docid = ?");
1987
			pstmt.setString(1, docId);
1988
			pstmt.execute();
1989
			ResultSet serverName = pstmt.getResultSet();
1990
			//get a server location
1991
			if (serverName.next()) {
1992
				serverLocation = serverName.getInt(1);
1993
				pstmt.close();
1994
			} else {
1995
				pstmt.close();
1996
				//ut.returnConnection(conn);
1997
				return null;
1998
			}
1999
			pstmt = dbConn.prepareStatement("select server, last_checked, replicate "
2000
					+ "from xml_replication where serverid = ?");
2001
			//increase usage count
2002
			dbConn.increaseUsageCount(1);
2003
			pstmt.setInt(1, serverLocation);
2004
			pstmt.execute();
2005
			ResultSet rs = pstmt.getResultSet();
2006
			boolean tableHasRows = rs.next();
2007
			if (tableHasRows) {
2008

    
2009
				String server = rs.getString(1);
2010
				String last_checked = rs.getString(2);
2011
				if (!server.equals("localhost")) {
2012
					sl.put(server, last_checked);
2013
				}
2014

    
2015
			} else {
2016
				pstmt.close();
2017
				//ut.returnConnection(conn);
2018
				return null;
2019
			}
2020
			pstmt.close();
2021
		} catch (Exception e) {
2022
			logMetacat.error("ReplicationService.getHomeServerInfoForDocId - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
2023
			logReplication.error("ReplicationService.getHomeServerInfoForDocId - error in replicationHandler.getHomeServer(): "
2024
					+ e.getMessage());
2025
		} finally {
2026
			try {
2027
				pstmt.close();
2028
				//ut.returnConnection(conn);
2029
			} catch (Exception ee) {
2030
				logMetacat.error("ReplicationService.getHomeServerInfoForDocId - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
2031
				logReplication.error("ReplicationService.getHomeServerInfoForDocId - Eror irn rplicationHandler.getHomeServer() "
2032
						+ "to close pstmt: " + ee.getMessage());
2033
			} finally {
2034
				DBConnectionPool.returnDBConnection(dbConn, serialNumber);
2035
			}
2036

    
2037
		}//finally
2038
		return sl;
2039
	}
2040

    
2041
	/**
2042
	 * Returns a home server location  given a accnum
2043
	 * @param accNum , given accNum for a document
2044
	 *
2045
	 */
2046
	public static int getHomeServerCodeForDocId(String accNum) throws ServiceException {
2047
		DBConnection dbConn = null;
2048
		int serialNumber = -1;
2049
		PreparedStatement pstmt = null;
2050
		int serverCode = 1;
2051
		String docId = DocumentUtil.getDocIdFromString(accNum);
2052

    
2053
		try {
2054

    
2055
			// Get DBConnection
2056
			dbConn = DBConnectionPool
2057
					.getDBConnection("ReplicationHandler.getServerLocation");
2058
			serialNumber = dbConn.getCheckOutSerialNumber();
2059
			pstmt = dbConn.prepareStatement("SELECT server_location FROM xml_documents "
2060
					+ "WHERE docid LIKE ? ");
2061
			pstmt.setString(1, docId);
2062
			pstmt.execute();
2063
			ResultSet rs = pstmt.getResultSet();
2064
			boolean tablehasrows = rs.next();
2065
			//If a document is find, return the server location for it
2066
			if (tablehasrows) {
2067
				serverCode = rs.getInt(1);
2068
				pstmt.close();
2069
				//conn.close();
2070
				return serverCode;
2071
			}
2072
			//if couldn't find in xml_documents table, we think server code is 1
2073
			//(this is new document)
2074
			else {
2075
				pstmt.close();
2076
				//conn.close();
2077
				return serverCode;
2078
			}
2079

    
2080
		} catch (SQLException sqle) {
2081
			throw new ServiceException("ReplicationService.getHomeServerCodeForDocId - " 
2082
					+ "SQL error when getting home server code for docid: " + docId + " : " 
2083
					+ sqle.getMessage());
2084

    
2085
		} finally {
2086
			try {
2087
				pstmt.close();
2088
				//conn.close();
2089

    
2090
			} catch (SQLException sqle) {
2091
				logMetacat.error("ReplicationService.getHomeServerCodeForDocId - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
2092
				logReplication.error("ReplicationService.getHomeServerCodeForDocId - ReplicationService.getHomeServerCodeForDocId - " 
2093
						+ "SQL error when getting home server code for docid: " + docId + " : " 
2094
						+ sqle.getMessage());
2095
			} finally {
2096
				DBConnectionPool.returnDBConnection(dbConn, serialNumber);
2097
			}//finally
2098
		}//finally
2099
		//return serverCode;
2100
	}
2101

    
2102
	/**
2103
	 * This method returns the content of a url
2104
	 * @param u the url to return the content from
2105
	 * @return a string representing the content of the url
2106
	 * @throws java.io.IOException
2107
	 */
2108
	public static String getURLContent(URL u) throws java.io.IOException {
2109
		char istreamChar;
2110
		int istreamInt;
2111
		// get the response content
2112
		InputStream input = getURLStream(u);
2113
		logReplication.info("ReplicationService.getURLContent - After getting response from: " + u.toString());
2114
		InputStreamReader istream = new InputStreamReader(input);
2115
		StringBuffer serverResponse = new StringBuffer();
2116
		while ((istreamInt = istream.read()) != -1) {
2117
			istreamChar = (char) istreamInt;
2118
			serverResponse.append(istreamChar);
2119
		}
2120
		istream.close();
2121
		input.close();
2122

    
2123
		return serverResponse.toString();
2124
	}
2125
	
2126
	/**
2127
	 * This method returns the InputStream after opening a url
2128
	 * @param u the url to return the content from
2129
	 * @return a InputStream representing the content of the url
2130
	 * @throws java.io.IOException
2131
	 */
2132
	public static InputStream getURLStream(URL u) throws java.io.IOException {
2133
	    logReplication.info("Getting url stream from " + u.toString());
2134
		logReplication.info("ReplicationService.getURLStream - Before sending request to: " + u.toString());
2135
		// use httpclient to set up SSL
2136
		RestClient client = getSSLClient();
2137
		HttpResponse response = client.doGetRequest(u.toString());
2138
		// get the response content
2139
		InputStream input = response.getEntity().getContent();
2140
		logReplication.info("ReplicationService.getURLStream - After getting response from: " + u.toString());
2141
		
2142
		return input;		
2143
	}
2144
	
2145
	/**
2146
	 * Sets up an HttpClient with SSL connection.
2147
	 * Sends client certificate to the server when doing the request.
2148
	 * @return
2149
	 */
2150
	private static RestClient getSSLClient() {
2151
		RestClient client = new RestClient();
2152
		
2153
		// set up this server's client identity
2154
		String subject = null;
2155
		try {
2156
			// TODO: should there be alternative ways to get the key and certificate?
2157
			String certificateFile = PropertyService.getProperty("replication.certificate.file");
2158
	    	String keyFile = PropertyService.getProperty("replication.privatekey.file");
2159
			String keyPassword = PropertyService.getProperty("replication.privatekey.password");
2160
			X509Certificate certificate = CertificateManager.getInstance().loadCertificateFromFile(certificateFile);
2161
			PrivateKey privateKey = CertificateManager.getInstance().loadPrivateKeyFromFile(keyFile, keyPassword);
2162
			subject = CertificateManager.getInstance().getSubjectDN(certificate);
2163
			CertificateManager.getInstance().registerCertificate(subject, certificate, privateKey);
2164
		} catch (Exception e) {
2165
			// this is pretty much required for replication communication
2166
			logReplication.warn("Could not find server's client certificate/private key: " + e.getMessage());
2167
		}
2168
		
2169
		// set the configured timeout
2170
		client.setTimeouts(CLIENTTIMEOUT);
2171

    
2172
		SSLSocketFactory socketFactory = null;
2173
		try {
2174
			socketFactory = CertificateManager.getInstance().getSSLSocketFactory(subject);
2175
		} catch (FileNotFoundException e) {
2176
			// these are somewhat expected for anonymous client use
2177
			logReplication.warn("Could not set up SSL connection for client - likely because the certificate could not be located: " + e.getMessage());
2178
		} catch (Exception e) {
2179
			// this is likely more severe
2180
			logReplication.warn("Funky SSL going on: " + e.getClass() + ":: " + e.getMessage());
2181
		}
2182
		try {
2183
			//443 is the default port, this value is overridden if explicitly set in the URL
2184
			Scheme sch = new Scheme("https", 443, socketFactory);
2185
			client.getHttpClient().getConnectionManager().getSchemeRegistry().register(sch);
2186
		} catch (Exception e) {
2187
			// this is likely more severe
2188
			logReplication.error("Failed to set up SSL connection for client. Continuing. " + e.getClass() + ":: " + e.getMessage(), e);
2189
		}
2190
		return client;
2191
	}
2192
	
2193

    
2194
//	/**
2195
//	 * Method for writing replication messages to a log file specified in
2196
//	 * metacat.properties
2197
//	 */
2198
//	public static void replLog(String message) {
2199
//		try {
2200
//			FileOutputStream fos = new FileOutputStream(PropertyService
2201
//					.getProperty("replication.logdir")
2202
//					+ "/metacatreplication.log", true);
2203
//			PrintWriter pw = new PrintWriter(fos);
2204
//			SimpleDateFormat formatter = new SimpleDateFormat("yy-MM-dd HH:mm:ss");
2205
//			java.util.Date localtime = new java.util.Date();
2206
//			String dateString = formatter.format(localtime);
2207
//			dateString += " :: " + message;
2208
//			// time stamp each entry
2209
//			pw.println(dateString);
2210
//			pw.flush();
2211
//		} catch (Exception e) {
2212
//			logReplication.error("error writing to replication log from "
2213
//					+ "MetacatReplication.replLog: " + e.getMessage());
2214
//			// e.printStackTrace(System.out);
2215
//		}
2216
//	}
2217

    
2218
//	/**
2219
//	 * Method for writing replication messages to a log file specified in
2220
//	 * metacat.properties
2221
//	 */
2222
//	public static void replErrorLog(String message) {
2223
//		try {
2224
//			FileOutputStream fos = new FileOutputStream(PropertyService
2225
//					.getProperty("replication.logdir")
2226
//					+ "/metacatreplicationerror.log", true);
2227
//			PrintWriter pw = new PrintWriter(fos);
2228
//			SimpleDateFormat formatter = new SimpleDateFormat("yy-MM-dd HH:mm:ss");
2229
//			java.util.Date localtime = new java.util.Date();
2230
//			String dateString = formatter.format(localtime);
2231
//			dateString += " :: " + message;
2232
//			//time stamp each entry
2233
//			pw.println(dateString);
2234
//			pw.flush();
2235
//		} catch (Exception e) {
2236
//			logReplication.error("error writing to replication error log from "
2237
//					+ "MetacatReplication.replErrorLog: " + e.getMessage());
2238
//			//e.printStackTrace(System.out);
2239
//		}
2240
//	}
2241

    
2242
	/**
2243
	 * Returns true if the replicate field for server in xml_replication is 1.
2244
	 * Returns false otherwise
2245
	 */
2246
	public static boolean replToServer(String server) {
2247
		DBConnection dbConn = null;
2248
		int serialNumber = -1;
2249
		PreparedStatement pstmt = null;
2250
		try {
2251
			dbConn = DBConnectionPool.getDBConnection("MetacatReplication.repltoServer");
2252
			serialNumber = dbConn.getCheckOutSerialNumber();
2253
			pstmt = dbConn.prepareStatement("select replicate from "
2254
					+ "xml_replication where server like ? ");
2255
			pstmt.setString(1, server);
2256
			pstmt.execute();
2257
			ResultSet rs = pstmt.getResultSet();
2258
			boolean tablehasrows = rs.next();
2259
			if (tablehasrows) {
2260
				int i = rs.getInt(1);
2261
				if (i == 1) {
2262
					pstmt.close();
2263
					//conn.close();
2264
					return true;
2265
				} else {
2266
					pstmt.close();
2267
					//conn.close();
2268
					return false;
2269
				}
2270
			}
2271
		} catch (SQLException sqle) {
2272
			logMetacat.error("ReplicationService.replToServer - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
2273
			logReplication.error("ReplicationService.replToServer - SQL error in MetacatReplication.replToServer: "
2274
					+ sqle.getMessage());
2275
		} finally {
2276
			try {
2277
				pstmt.close();
2278
				//conn.close();
2279
			}//try
2280
			catch (Exception ee) {
2281
				logMetacat.error("ReplicationService.replToServer - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
2282
				logReplication.error("ReplicationService.replToServer - Error in MetacatReplication.replToServer: "
2283
						+ ee.getMessage());
2284
			}//catch
2285
			finally {
2286
				DBConnectionPool.returnDBConnection(dbConn, serialNumber);
2287
			}//finally
2288
		}//finally
2289
		return false;
2290
		//the default if this server does not exist is to not replicate to it.
2291
	}
2292

    
2293
}
(6-6/7)