Project

General

Profile

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

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

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

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

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

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

    
112
public class ReplicationService extends BaseService {
113

    
114
	private static ReplicationService replicationService = null;
115

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

    
132
	public static final String REPLICATION_LOG_FILE_NAME = "metacatreplication.log";
133

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

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

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

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

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

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

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

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

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

    
225
	public boolean refreshable() {
226
		return true;
227
	}
228

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

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

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

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

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

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

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

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

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

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

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

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

    
570
	}
571

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

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

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

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

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

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

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

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

    
807
		}//catch
808

    
809
	}
810

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

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

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

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

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

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

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

    
873
			String docType = (String) docinfoHash.get("doctype");
874

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

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

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

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

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

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

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

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

    
995
		try {
996

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

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

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

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

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

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

    
1075
		String docInfoStr = getDocumentInfo(docid);
1076

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

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

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

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

    
1141
		sb.append("<accessControl>");
1142

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

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

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

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

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

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

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

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

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

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

    
1288
		// Set the mime type
1289
		response.setContentType(contentType);
1290

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

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

    
1317
	}
1318

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

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

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

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

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

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

    
1374
			logReplication.info("ReplicationService.handleGetDocumentRequest - document " + docid + " sent");
1375

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

    
1435
	}
1436

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1686
	}//handlUpdateRequest
1687

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

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

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

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

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

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

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

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

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

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

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

    
1863
		return null;
1864
	}
1865

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

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

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

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

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

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

    
1959
		return null;
1960
		//return null if the server does not exist
1961
	}
1962

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

    
1975
		try {
1976

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

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

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

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

    
2013
		return serverCode;
2014
	}
2015
	
2016
	/**
2017
	 * Returns a Map of serverCode=serverName 
2018
	 * @return Map of server codes to names (URIs)
2019
	 */
2020
	public static Map<Integer, String> getServerCodes() throws ServiceException {
2021
		DBConnection dbConn = null;
2022
		int serialNumber = -1;
2023
		PreparedStatement pstmt = null;
2024
		
2025
		Map<Integer, String> codes = new HashMap<Integer, String>();
2026

    
2027
		try {
2028

    
2029
			dbConn = DBConnectionPool.getDBConnection("MetacatReplication.getServerCodes");
2030
			serialNumber = dbConn.getCheckOutSerialNumber();
2031
			pstmt = dbConn.prepareStatement("SELECT serverid, server FROM xml_replication ");
2032
			pstmt.execute();
2033
			ResultSet rs = pstmt.getResultSet();
2034
			while (rs.next()) {
2035
				int serverCode = rs.getInt(1);
2036
				String server = rs.getString(2);
2037
				codes.put(serverCode, server);
2038
			}
2039
			pstmt.close();
2040
			
2041
		} catch (SQLException sqle) {
2042
			throw new ServiceException("ReplicationService.getServerCodes - " 
2043
					+ "SQL error when getting server map: " + sqle.getMessage());
2044

    
2045
		} finally {
2046
			try {
2047
				pstmt.close();
2048
			}//try
2049
			catch (Exception ee) {
2050
				logMetacat.error("ReplicationService.getServerCodes - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
2051
				logReplication.error("ReplicationService.getServerCodes - Error in MetacatReplicatio.getServerCodes: "
2052
						+ ee.getMessage());
2053

    
2054
			}//catch
2055
			finally {
2056
				DBConnectionPool.returnDBConnection(dbConn, serialNumber);
2057
			}//finally
2058
		}//finally
2059

    
2060
		return codes;
2061
	}
2062

    
2063
	/**
2064
	 * Method to get a host server information for given docid
2065
	 * @param conn a connection to the database
2066
	 */
2067
	public static Hashtable<String, String> getHomeServerInfoForDocId(String docId) {
2068
		Hashtable<String, String> sl = new Hashtable<String, String>();
2069
		DBConnection dbConn = null;
2070
		int serialNumber = -1;
2071
		docId = DocumentUtil.getDocIdFromString(docId);
2072
		PreparedStatement pstmt = null;
2073
		int serverLocation;
2074
		try {
2075
			//get conection
2076
			dbConn = DBConnectionPool.getDBConnection("ReplicationHandler.getHomeServer");
2077
			serialNumber = dbConn.getCheckOutSerialNumber();
2078
			//get a server location from xml_document table
2079
			pstmt = dbConn.prepareStatement("select server_location from xml_documents "
2080
					+ "where docid = ?");
2081
			pstmt.setString(1, docId);
2082
			pstmt.execute();
2083
			ResultSet serverName = pstmt.getResultSet();
2084
			//get a server location
2085
			if (serverName.next()) {
2086
				serverLocation = serverName.getInt(1);
2087
				pstmt.close();
2088
			} else {
2089
				pstmt.close();
2090
				//ut.returnConnection(conn);
2091
				return null;
2092
			}
2093
			pstmt = dbConn.prepareStatement("select server, last_checked, replicate "
2094
					+ "from xml_replication where serverid = ?");
2095
			//increase usage count
2096
			dbConn.increaseUsageCount(1);
2097
			pstmt.setInt(1, serverLocation);
2098
			pstmt.execute();
2099
			ResultSet rs = pstmt.getResultSet();
2100
			boolean tableHasRows = rs.next();
2101
			if (tableHasRows) {
2102

    
2103
				String server = rs.getString(1);
2104
				String last_checked = rs.getString(2);
2105
				if (!server.equals("localhost")) {
2106
					sl.put(server, last_checked);
2107
				}
2108

    
2109
			} else {
2110
				pstmt.close();
2111
				//ut.returnConnection(conn);
2112
				return null;
2113
			}
2114
			pstmt.close();
2115
		} catch (Exception e) {
2116
			logMetacat.error("ReplicationService.getHomeServerInfoForDocId - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
2117
			logReplication.error("ReplicationService.getHomeServerInfoForDocId - error in replicationHandler.getHomeServer(): "
2118
					+ e.getMessage());
2119
		} finally {
2120
			try {
2121
				pstmt.close();
2122
				//ut.returnConnection(conn);
2123
			} catch (Exception ee) {
2124
				logMetacat.error("ReplicationService.getHomeServerInfoForDocId - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
2125
				logReplication.error("ReplicationService.getHomeServerInfoForDocId - Eror irn rplicationHandler.getHomeServer() "
2126
						+ "to close pstmt: " + ee.getMessage());
2127
			} finally {
2128
				DBConnectionPool.returnDBConnection(dbConn, serialNumber);
2129
			}
2130

    
2131
		}//finally
2132
		return sl;
2133
	}
2134

    
2135
	/**
2136
	 * Returns a home server location  given a accnum
2137
	 * @param accNum , given accNum for a document
2138
	 *
2139
	 */
2140
	public static int getHomeServerCodeForDocId(String accNum) throws ServiceException {
2141
		DBConnection dbConn = null;
2142
		int serialNumber = -1;
2143
		PreparedStatement pstmt = null;
2144
		int serverCode = 1;
2145
		String docId = DocumentUtil.getDocIdFromString(accNum);
2146

    
2147
		try {
2148

    
2149
			// Get DBConnection
2150
			dbConn = DBConnectionPool
2151
					.getDBConnection("ReplicationHandler.getServerLocation");
2152
			serialNumber = dbConn.getCheckOutSerialNumber();
2153
			pstmt = dbConn.prepareStatement("SELECT server_location FROM xml_documents "
2154
					+ "WHERE docid LIKE ? ");
2155
			pstmt.setString(1, docId);
2156
			pstmt.execute();
2157
			ResultSet rs = pstmt.getResultSet();
2158
			boolean tablehasrows = rs.next();
2159
			//If a document is find, return the server location for it
2160
			if (tablehasrows) {
2161
				serverCode = rs.getInt(1);
2162
				pstmt.close();
2163
				//conn.close();
2164
				return serverCode;
2165
			}
2166
			//if couldn't find in xml_documents table, we think server code is 1
2167
			//(this is new document)
2168
			else {
2169
				pstmt.close();
2170
				//conn.close();
2171
				return serverCode;
2172
			}
2173

    
2174
		} catch (SQLException sqle) {
2175
			throw new ServiceException("ReplicationService.getHomeServerCodeForDocId - " 
2176
					+ "SQL error when getting home server code for docid: " + docId + " : " 
2177
					+ sqle.getMessage());
2178

    
2179
		} finally {
2180
			try {
2181
				pstmt.close();
2182
				//conn.close();
2183

    
2184
			} catch (SQLException sqle) {
2185
				logMetacat.error("ReplicationService.getHomeServerCodeForDocId - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
2186
				logReplication.error("ReplicationService.getHomeServerCodeForDocId - ReplicationService.getHomeServerCodeForDocId - " 
2187
						+ "SQL error when getting home server code for docid: " + docId + " : " 
2188
						+ sqle.getMessage());
2189
			} finally {
2190
				DBConnectionPool.returnDBConnection(dbConn, serialNumber);
2191
			}//finally
2192
		}//finally
2193
		//return serverCode;
2194
	}
2195

    
2196
	/**
2197
	 * This method returns the content of a url
2198
	 * @param u the url to return the content from
2199
	 * @return a string representing the content of the url
2200
	 * @throws java.io.IOException
2201
	 */
2202
	public static String getURLContent(URL u) throws java.io.IOException {
2203
		char istreamChar;
2204
		int istreamInt;
2205
		// get the response content
2206
		InputStream input = getURLStream(u);
2207
		logReplication.info("ReplicationService.getURLContent - After getting response from: " + u.toString());
2208
		InputStreamReader istream = new InputStreamReader(input);
2209
		StringBuffer serverResponse = new StringBuffer();
2210
		while ((istreamInt = istream.read()) != -1) {
2211
			istreamChar = (char) istreamInt;
2212
			serverResponse.append(istreamChar);
2213
		}
2214
		istream.close();
2215
		input.close();
2216

    
2217
		return serverResponse.toString();
2218
	}
2219
	
2220
	/**
2221
	 * This method returns the InputStream after opening a url
2222
	 * @param u the url to return the content from
2223
	 * @return a InputStream representing the content of the url
2224
	 * @throws java.io.IOException
2225
	 */
2226
	public static InputStream getURLStream(URL u) throws java.io.IOException {
2227
	    logReplication.info("Getting url stream from " + u.toString());
2228
		logReplication.info("ReplicationService.getURLStream - Before sending request to: " + u.toString());
2229
		// use httpclient to set up SSL
2230
		RestClient client = getSSLClient();
2231
		HttpResponse response = client.doGetRequest(u.toString());
2232
		// get the response content
2233
		InputStream input = response.getEntity().getContent();
2234
		logReplication.info("ReplicationService.getURLStream - After getting response from: " + u.toString());
2235
		
2236
		return input;		
2237
	}
2238
	
2239
	/**
2240
	 * Sets up an HttpClient with SSL connection.
2241
	 * Sends client certificate to the server when doing the request.
2242
	 * @return
2243
	 */
2244
	private static RestClient getSSLClient() {
2245
		RestClient client = new RestClient();
2246
		
2247
		// set up this server's client identity
2248
		String subject = null;
2249
		try {
2250
			// TODO: should there be alternative ways to get the key and certificate?
2251
			String certificateFile = PropertyService.getProperty("replication.certificate.file");
2252
	    	String keyFile = PropertyService.getProperty("replication.privatekey.file");
2253
			String keyPassword = PropertyService.getProperty("replication.privatekey.password");
2254
			X509Certificate certificate = CertificateManager.getInstance().loadCertificateFromFile(certificateFile);
2255
			PrivateKey privateKey = CertificateManager.getInstance().loadPrivateKeyFromFile(keyFile, keyPassword);
2256
			subject = CertificateManager.getInstance().getSubjectDN(certificate);
2257
			CertificateManager.getInstance().registerCertificate(subject, certificate, privateKey);
2258
		} catch (Exception e) {
2259
			// this is pretty much required for replication communication
2260
			logReplication.warn("Could not find server's client certificate/private key: " + e.getMessage());
2261
		}
2262
		
2263
		// set the configured timeout
2264
		client.setTimeouts(CLIENTTIMEOUT);
2265

    
2266
		SSLSocketFactory socketFactory = null;
2267
		try {
2268
			socketFactory = CertificateManager.getInstance().getSSLSocketFactory(subject);
2269
		} catch (FileNotFoundException e) {
2270
			// these are somewhat expected for anonymous client use
2271
			logReplication.warn("Could not set up SSL connection for client - likely because the certificate could not be located: " + e.getMessage());
2272
		} catch (Exception e) {
2273
			// this is likely more severe
2274
			logReplication.warn("Funky SSL going on: " + e.getClass() + ":: " + e.getMessage());
2275
		}
2276
		try {
2277
			//443 is the default port, this value is overridden if explicitly set in the URL
2278
			Scheme sch = new Scheme("https", 443, socketFactory);
2279
			client.getHttpClient().getConnectionManager().getSchemeRegistry().register(sch);
2280
		} catch (Exception e) {
2281
			// this is likely more severe
2282
			logReplication.error("Failed to set up SSL connection for client. Continuing. " + e.getClass() + ":: " + e.getMessage(), e);
2283
		}
2284
		return client;
2285
	}
2286
	
2287

    
2288
//	/**
2289
//	 * Method for writing replication messages to a log file specified in
2290
//	 * metacat.properties
2291
//	 */
2292
//	public static void replLog(String message) {
2293
//		try {
2294
//			FileOutputStream fos = new FileOutputStream(PropertyService
2295
//					.getProperty("replication.logdir")
2296
//					+ "/metacatreplication.log", true);
2297
//			PrintWriter pw = new PrintWriter(fos);
2298
//			SimpleDateFormat formatter = new SimpleDateFormat("yy-MM-dd HH:mm:ss");
2299
//			java.util.Date localtime = new java.util.Date();
2300
//			String dateString = formatter.format(localtime);
2301
//			dateString += " :: " + message;
2302
//			// time stamp each entry
2303
//			pw.println(dateString);
2304
//			pw.flush();
2305
//		} catch (Exception e) {
2306
//			logReplication.error("error writing to replication log from "
2307
//					+ "MetacatReplication.replLog: " + e.getMessage());
2308
//			// e.printStackTrace(System.out);
2309
//		}
2310
//	}
2311

    
2312
//	/**
2313
//	 * Method for writing replication messages to a log file specified in
2314
//	 * metacat.properties
2315
//	 */
2316
//	public static void replErrorLog(String message) {
2317
//		try {
2318
//			FileOutputStream fos = new FileOutputStream(PropertyService
2319
//					.getProperty("replication.logdir")
2320
//					+ "/metacatreplicationerror.log", true);
2321
//			PrintWriter pw = new PrintWriter(fos);
2322
//			SimpleDateFormat formatter = new SimpleDateFormat("yy-MM-dd HH:mm:ss");
2323
//			java.util.Date localtime = new java.util.Date();
2324
//			String dateString = formatter.format(localtime);
2325
//			dateString += " :: " + message;
2326
//			//time stamp each entry
2327
//			pw.println(dateString);
2328
//			pw.flush();
2329
//		} catch (Exception e) {
2330
//			logReplication.error("error writing to replication error log from "
2331
//					+ "MetacatReplication.replErrorLog: " + e.getMessage());
2332
//			//e.printStackTrace(System.out);
2333
//		}
2334
//	}
2335

    
2336
	/**
2337
	 * Returns true if the replicate field for server in xml_replication is 1.
2338
	 * Returns false otherwise
2339
	 */
2340
	public static boolean replToServer(String server) {
2341
		DBConnection dbConn = null;
2342
		int serialNumber = -1;
2343
		PreparedStatement pstmt = null;
2344
		try {
2345
			dbConn = DBConnectionPool.getDBConnection("MetacatReplication.repltoServer");
2346
			serialNumber = dbConn.getCheckOutSerialNumber();
2347
			pstmt = dbConn.prepareStatement("select replicate from "
2348
					+ "xml_replication where server like ? ");
2349
			pstmt.setString(1, server);
2350
			pstmt.execute();
2351
			ResultSet rs = pstmt.getResultSet();
2352
			boolean tablehasrows = rs.next();
2353
			if (tablehasrows) {
2354
				int i = rs.getInt(1);
2355
				if (i == 1) {
2356
					pstmt.close();
2357
					//conn.close();
2358
					return true;
2359
				} else {
2360
					pstmt.close();
2361
					//conn.close();
2362
					return false;
2363
				}
2364
			}
2365
		} catch (SQLException sqle) {
2366
			logMetacat.error("ReplicationService.replToServer - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
2367
			logReplication.error("ReplicationService.replToServer - SQL error in MetacatReplication.replToServer: "
2368
					+ sqle.getMessage());
2369
		} finally {
2370
			try {
2371
				pstmt.close();
2372
				//conn.close();
2373
			}//try
2374
			catch (Exception ee) {
2375
				logMetacat.error("ReplicationService.replToServer - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
2376
				logReplication.error("ReplicationService.replToServer - Error in MetacatReplication.replToServer: "
2377
						+ ee.getMessage());
2378
			}//catch
2379
			finally {
2380
				DBConnectionPool.returnDBConnection(dbConn, serialNumber);
2381
			}//finally
2382
		}//finally
2383
		return false;
2384
		//the default if this server does not exist is to not replicate to it.
2385
	}
2386

    
2387
}
(6-6/7)