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: 2015-02-06 09:15:54 -0800 (Fri, 06 Feb 2015) $'
10
 * '$Revision: 9095 $'
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.rest.DefaultHttpMultipartRestClient;
69
import org.dataone.client.rest.HttpMultipartRestClient;
70
import org.dataone.client.rest.RestClient;
71
import org.dataone.client.auth.CertificateManager;
72
import org.dataone.service.types.v1.Identifier;
73
import org.dataone.service.types.v2.SystemMetadata;
74
import org.dataone.service.util.DateTimeMarshaller;
75
import org.dataone.service.util.TypeMarshaller;
76
import org.jibx.runtime.JiBXException;
77
import org.xml.sax.InputSource;
78
import org.xml.sax.SAXException;
79
import org.xml.sax.XMLReader;
80

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

    
115
public class ReplicationService extends BaseService {
116

    
117
	private static ReplicationService replicationService = null;
118

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

    
135
	public static final String REPLICATION_LOG_FILE_NAME = "metacatreplication.log";
136

    
137
	private static String DATA_FILE_FLAG = null;
138
	public static String METACAT_REPL_ERROR_MSG = null;
139
	private static Logger logReplication = Logger.getLogger("ReplicationLogging");
140
	private static Logger logMetacat = Logger.getLogger(ReplicationService.class);
141

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

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

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

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

    
194
			logReplication.info("ReplicationService.initialize - After combine current time, the real first time is "
195
					+ firstTime.toString() + " minisec");
196

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

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

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

    
228
	public boolean refreshable() {
229
		return true;
230
	}
231

    
232
	protected void doRefresh() throws ServiceException {
233
		return;
234
	}
235
	
236
	public void stop() throws ServiceException{
237
		
238
	}
239

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

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

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

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

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

    
356
				//Get data replication value
357
				dataReplicate = ((String[]) params.get("datareplicate"))[0];
358
				
359
				//Get hub value
360
				hub = ((String[]) params.get("hub"))[0];
361

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

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

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

    
589
				tablehasrows = rs.next();
590
			}
591
			out.write("</table></body></html>");
592
			
593
			
594
			pstmt.close();
595
			//conn.close();
596

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

    
623
	}
624

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

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

    
659
			// get the document info from server
660
			URL docinfourl = new URL("https://" + server + "?server="
661
					+ MetacatUtil.getLocalReplicationServerName()
662
					+ "&action=getdocumentinfo&docid=" + docid);
663
			
664

    
665
			String docInfoStr = ReplicationService.getURLContent(docinfourl);
666
			// strip out the system metadata portion
667
			String systemMetadataXML = ReplicationUtil.getSystemMetadataContent(docInfoStr);
668
			docInfoStr = ReplicationUtil.getContentWithoutSystemMetadata(docInfoStr);
669
		   	  
670
			//dih is the parser for the docinfo xml format
671
			DocInfoHandler dih = new DocInfoHandler();
672
			XMLReader docinfoParser = ReplicationHandler.initParser(dih);
673
			docinfoParser.parse(new InputSource(new StringReader(docInfoStr)));
674
			//      Hashtable<String,Vector<AccessControlForSingleFile>> docinfoHash = dih.getDocInfo();
675
			Hashtable<String, String> docinfoHash = dih.getDocInfo();
676
			
677
			// Get home server of this docid
678
			String homeServer = (String) docinfoHash.get("home_server");
679
			
680
			// process system metadata
681
			SystemMetadata sysMeta = null;
682
			if (systemMetadataXML != null) {
683
				sysMeta = 
684
					TypeMarshaller.unmarshalTypeFromStream(
685
							SystemMetadata.class,
686
							new ByteArrayInputStream(systemMetadataXML.getBytes("UTF-8")));
687
				// need the guid-to-docid mapping
688
				boolean mappingExists = true;
689
		      	mappingExists = IdentifierManager.getInstance().mappingExists(sysMeta.getIdentifier().getValue());
690
		      	if (!mappingExists) {
691
		      		IdentifierManager.getInstance().createMapping(sysMeta.getIdentifier().getValue(), docid);
692
		      	}
693
				// save the system metadata
694
				HazelcastService.getInstance().getSystemMetadataMap().put(sysMeta.getIdentifier(), sysMeta);
695
				
696
			}
697
      
698
			// dates
699
			String createdDateString = docinfoHash.get("date_created");
700
			String updatedDateString = docinfoHash.get("date_updated");
701
			Date createdDate = DateTimeMarshaller.deserializeDateToUTC(createdDateString);
702
			Date updatedDate = DateTimeMarshaller.deserializeDateToUTC(updatedDateString);
703
		      
704
			logReplication.info("ReplicationService.handleForceReplicateRequest - homeServer: " + homeServer);
705
			// Get Document type
706
			String docType = (String) docinfoHash.get("doctype");
707
			logReplication.info("ReplicationService.handleForceReplicateRequest - docType: " + docType);
708
			String parserBase = null;
709
			// this for eml2 and we need user eml2 parser
710
			if (docType != null
711
					&& (docType.trim()).equals(DocumentImpl.EML2_0_0NAMESPACE)) {
712
				logReplication.warn("ReplicationService.handleForceReplicateRequest - This is an eml200 document!");
713
				parserBase = DocumentImpl.EML200;
714
			} else if (docType != null
715
					&& (docType.trim()).equals(DocumentImpl.EML2_0_1NAMESPACE)) {
716
				logReplication.warn("ReplicationService.handleForceReplicateRequest - This is an eml2.0.1 document!");
717
				parserBase = DocumentImpl.EML200;
718
			} else if (docType != null
719
					&& (docType.trim()).equals(DocumentImpl.EML2_1_0NAMESPACE)) {
720
				logReplication.warn("ReplicationService.handleForceReplicateRequest - This is an eml2.1.0 document!");
721
				parserBase = DocumentImpl.EML210;
722
			} else if (docType != null
723
					&& (docType.trim()).equals(DocumentImpl.EML2_1_1NAMESPACE)) {
724
				logReplication.warn("ReplicationService.handleForceReplicateRequest - This is an eml2.1.1 document!");
725
				parserBase = DocumentImpl.EML210;
726
			}
727
			logReplication.warn("ReplicationService.handleForceReplicateRequest - The parserBase is: " + parserBase);
728

    
729
			// Get DBConnection from pool
730
			dbConn = DBConnectionPool
731
					.getDBConnection("MetacatReplication.handleForceReplicateRequest");
732
			serialNumber = dbConn.getCheckOutSerialNumber();
733
			// write the document to local database
734
			DocumentImplWrapper wrapper = new DocumentImplWrapper(parserBase, false, false);
735
			//try this independently so we can set access even if the update action is invalid (i.e docid has not changed)
736
			try {
737
				wrapper.writeReplication(dbConn, xmldoc, xmlBytes, null, null,
738
						dbaction, docid, null, null, homeServer, server, createdDate,
739
						updatedDate);
740
			} finally {
741
				if(sysMeta != null) {
742
					// submit for indexing. When the doc writing process fails, the index process will fail as well. But this failure
743
					// will not interrupt the process.
744
					try {
745
						MetacatSolrIndex.getInstance().submit(sysMeta.getIdentifier(), sysMeta, null, true);
746
					} catch (Exception ee) {
747
						logReplication.warn("ReplicationService.handleForceReplicateRequest - couldn't index the doc since "+ee.getMessage());
748
					}
749
	                
750
				}
751
				//process extra access rules before dealing with the write exception (doc exist already)
752
				try {
753
		        	// check if we had a guid -> docid mapping
754
		        	String docidNoRev = DocumentUtil.getDocIdFromAccessionNumber(docid);
755
		        	int rev = DocumentUtil.getRevisionFromAccessionNumber(docid);
756
		        	IdentifierManager.getInstance().getGUID(docidNoRev, rev);
757
		        	// no need to create the mapping if we have it
758
		        } catch (McdbDocNotFoundException mcdbe) {
759
		        	// create mapping if we don't
760
		        	IdentifierManager.getInstance().createMapping(docid, docid);
761
		        }
762
		        Vector<XMLAccessDAO> accessControlList = dih.getAccessControlList();
763
		        if (accessControlList != null) {
764
		        	AccessControlForSingleFile acfsf = new AccessControlForSingleFile(docid);
765
		        	for (XMLAccessDAO xmlAccessDAO : accessControlList) {
766
		        		try {
767
			        		if (!acfsf.accessControlExists(xmlAccessDAO)) {
768
			        			acfsf.insertPermissions(xmlAccessDAO);
769
								logReplication.info("ReplicationService.handleForceReplicateRequest - document " + docid
770
										+ " permissions added to DB");
771
			        		}
772
		        		} catch (PermOrderException poe) {
773
		        			// this is problematic, but should not prevent us from replicating
774
		        			// see https://redmine.dataone.org/issues/2583
775
		        			String msg = "Could not insert access control for: " + docid + " Message: " + poe.getMessage();
776
		        			logMetacat.error(msg, poe);
777
		        			logReplication.error(msg, poe);
778
		        		}
779
		            }
780
		        }
781
		        
782
		        // process the real owner and updater
783
				String user = (String) docinfoHash.get("user_owner");
784
				String updated = (String) docinfoHash.get("user_updated");
785
		        updateUserOwner(dbConn, docid, user, updated);
786

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

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

    
870
		}//catch
871

    
872
	}
873

    
874
	/**
875
	 * when a forcereplication data file request comes in, local host sends a
876
	 * readdata request to the requesting server (remote server) for the specified
877
	 * docid. Then store it in local database and file system
878
	 */
879
	protected static void handleForceReplicateDataFileRequest(Hashtable<String, String[]> params,
880
			HttpServletRequest request) {
881

    
882
		//make sure there is some parameters
883
		if (params.isEmpty()) {
884
			return;
885
		}
886
		// Get remote server
887
		String server = ((String[]) params.get("server"))[0];
888
		// the docid should include rev number
889
		String docid = ((String[]) params.get("docid"))[0];
890
		// Make sure there is a docid and server
891
		if (docid == null || server == null || server.equals("")) {
892
			logMetacat.error("ReplicationService.handleForceReplicateDataFileRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
893
			logReplication.error("ReplicationService.handleForceReplicateDataFileRequest - Didn't specify docid or server for replication");
894
			return;
895
		}
896

    
897
		// Overide or not
898
		//    boolean override = false;
899
		// dbaction - update or insert
900
		String dbaction = null;
901

    
902
		try {
903
			//docid was switch to two parts uinque code and rev
904
			//String uniqueCode=MetacatUtil.getDocIdFromString(docid);
905
			//int rev=MetacatUtil.getVersionFromString(docid);
906
			if (params.containsKey("dbaction")) {
907
				dbaction = ((String[]) params.get("dbaction"))[0];
908
			} else//default value is update
909
			{
910
//				dbaction = "update";
911
				dbaction = null;
912
			}
913

    
914
			logReplication.info("ReplicationService.handleForceReplicateDataFileRequest - Force replication request from: " + server);
915
			logReplication.info("ReplicationService.handleForceReplicateDataFileRequest - Force replication docid: " + docid);
916
			logReplication.info("ReplicationService.handleForceReplicateDataFileRequest - Force replication action: " + dbaction);
917
			// get the document info from server
918
			URL docinfourl = new URL("https://" + server + "?server="
919
					+ MetacatUtil.getLocalReplicationServerName()
920
					+ "&action=getdocumentinfo&docid=" + docid);
921

    
922
			String docInfoStr = ReplicationService.getURLContent(docinfourl);
923
			
924
			// strip out the system metadata portion
925
		    String systemMetadataXML = ReplicationUtil.getSystemMetadataContent(docInfoStr);
926
		   	docInfoStr = ReplicationUtil.getContentWithoutSystemMetadata(docInfoStr);
927

    
928
			//dih is the parser for the docinfo xml format
929
			DocInfoHandler dih = new DocInfoHandler();
930
			XMLReader docinfoParser = ReplicationHandler.initParser(dih);
931
			docinfoParser.parse(new InputSource(new StringReader(docInfoStr)));
932
			Hashtable<String, String> docinfoHash = dih.getDocInfo();
933
			
934
			String docName = (String) docinfoHash.get("docname");
935

    
936
			String docType = (String) docinfoHash.get("doctype");
937

    
938
			String docHomeServer = (String) docinfoHash.get("home_server");
939
			
940
			String createdDateString = docinfoHash.get("date_created");
941
			String updatedDateString = docinfoHash.get("date_updated");
942
			
943
			Date createdDate = DateTimeMarshaller.deserializeDateToUTC(createdDateString);
944
			Date updatedDate = DateTimeMarshaller.deserializeDateToUTC(updatedDateString);
945
			
946
			logReplication.info("ReplicationService.handleForceReplicateDataFileRequest - docHomeServer of datafile: " + docHomeServer);
947

    
948
			// in case we have a write exception, we still want to track access and sysmeta
949
			Exception writeException = null;
950

    
951
			// do we need the object content?
952
			if (dbaction != null && (dbaction.equals("insert") || dbaction.equals("update"))) {
953
				//Get data file and store it into local file system.
954
				// sending back readdata request to server
955
				URL url = new URL("https://" + server + "?server="
956
						+ MetacatUtil.getLocalReplicationServerName()
957
						+ "&action=readdata&docid=" + docid);
958
				String datafilePath = PropertyService
959
						.getProperty("application.datafilepath");
960

    
961
				InputStream inputStream = getURLStream(url);
962
				
963
				//register data file into xml_documents table and write data file
964
				//into file system
965
				try {
966
					DocumentImpl.writeDataFileInReplication(inputStream,
967
							datafilePath, docName, docType, docid, null, docHomeServer,
968
							server, DocumentImpl.DOCUMENTTABLE, false, createdDate,
969
							updatedDate);
970
				} catch (Exception e) {
971
					writeException = e;
972
				}
973

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

    
1032
			logReplication.info("ReplicationService.handleForceReplicateDataFileRequest - datafile " + docid + " added to DB with "
1033
					+ "action " + dbaction);
1034
			EventLog.getInstance().log(request.getRemoteAddr(), request.getHeader("User-Agent"), REPLICATIONUSER,
1035
					docid, dbaction);
1036

    
1037
		} catch (Exception e) {
1038
			e.printStackTrace();
1039
			logMetacat.error("ReplicationService.handleForceReplicateDataFileRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG, e);                         
1040
			logReplication.error("ReplicationService.handleForceReplicateDataFileRequest - Datafile " + docid
1041
					+ " failed to added to DB with " + "action " + dbaction + " because "
1042
					+ e.getMessage());
1043
			logReplication.error("ReplicationService.handleForceReplicateDataFileRequest - ERROR in MetacatReplication.handleForceDataFileReplicate"
1044
					+ "Request(): " + e.getMessage());
1045
		}
1046
	}
1047

    
1048
	/**
1049
	 * Grants or denies a lock to a requesting host.
1050
	 * The servlet parameters of interrest are:
1051
	 * docid: the docid of the file the lock is being requested for
1052
	 * currentdate: the timestamp of the document on the remote server
1053
	 *
1054
	 */
1055
	protected static void handleGetLockRequest(
1056
			Hashtable<String, String[]> params, HttpServletResponse response) {
1057

    
1058
		try {
1059

    
1060
			String docid = ((String[]) params.get("docid"))[0];
1061
			String remoteRev = ((String[]) params.get("updaterev"))[0];
1062
			DocumentImpl requestDoc = new DocumentImpl(docid);
1063
			logReplication.info("ReplicationService.handleGetLockRequest - lock request for " + docid);
1064
			int localRevInt = requestDoc.getRev();
1065
			int remoteRevInt = Integer.parseInt(remoteRev);
1066

    
1067
			// get a writer for sending back to response
1068
			response.setContentType("text/xml");
1069
			Writer out = response.getWriter();
1070
			
1071
			if (remoteRevInt >= localRevInt) {
1072
				if (!fileLocks.contains(docid)) { //grant the lock if it is not already locked
1073
					fileLocks.add(0, docid); //insert at the beginning of the queue Vector
1074
					//send a message back to the the remote host authorizing the insert
1075
					out.write("<lockgranted><docid>" + docid
1076
									+ "</docid></lockgranted>");
1077
					//          lockThread = new Thread(this);
1078
					//          lockThread.setPriority(Thread.MIN_PRIORITY);
1079
					//          lockThread.start();
1080
					logReplication.info("ReplicationService.handleGetLockRequest - lock granted for " + docid);
1081
				} else { //deny the lock
1082
					out.write("<filelocked><docid>" + docid + "</docid></filelocked>");
1083
					logReplication.info("ReplicationService.handleGetLockRequest - lock denied for " + docid
1084
							+ "reason: file already locked");
1085
				}
1086
			} else {//deny the lock.
1087
				out.write("<outdatedfile><docid>" + docid + "</docid></filelocked>");
1088
				logReplication.info("ReplicationService.handleGetLockRequest - lock denied for " + docid
1089
						+ "reason: client has outdated file");
1090
			}
1091
			out.close();
1092
			//conn.close();
1093
		} catch (Exception e) {
1094
			logMetacat.error("ReplicationService.handleGetLockRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
1095
			logReplication.error("ReplicationService.handleGetLockRequest - error requesting file lock from MetacatReplication."
1096
					+ "handleGetLockRequest: " + e.getMessage());
1097
			e.printStackTrace(System.out);
1098
		}
1099
	}
1100

    
1101
	/**
1102
	 * Sends all of the xml_documents information encoded in xml to a requestor
1103
	 * the format is:
1104
	 * <!ELEMENT documentinfo (docid, docname, doctype, doctitle, user_owner,
1105
	 *                  user_updated, home_server, public_access, rev)/>
1106
	 * all of the subelements of document info are #PCDATA
1107
	 */
1108
	protected static void handleGetDocumentInfoRequest(
1109
			Hashtable<String, String[]> params, HttpServletResponse response) {
1110
		String docid = ((String[]) (params.get("docid")))[0];
1111

    
1112
		try {
1113
			// get docinfo as XML string
1114
			String docinfoXML = getDocumentInfo(docid);
1115
			
1116
			// get a writer for sending back to response
1117
			response.setContentType("text/xml");
1118
			Writer out = response.getWriter();
1119
			out.write(docinfoXML);
1120
			out.close();
1121

    
1122
		} catch (Exception e) {
1123
			logMetacat.error("ReplicationService.handleGetDocumentInfoRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
1124
			logReplication.error("ReplicationService.handleGetDocumentInfoRequest - error in metacatReplication.handlegetdocumentinforequest "
1125
					+ "for doc: " + docid + " : " + e.getMessage());
1126
		}
1127

    
1128
	}
1129
	
1130
	public static Hashtable<String, String> getDocumentInfoMap(String docid)
1131
			throws HandlerException, AccessControlException, JiBXException,
1132
			IOException, McdbException, SAXException {
1133
		
1134
		// Try get docid info from remote server
1135
		DocInfoHandler dih = new DocInfoHandler();
1136
		XMLReader docinfoParser = ReplicationHandler.initParser(dih);
1137

    
1138
		String docInfoStr = getDocumentInfo(docid);
1139

    
1140
		// strip out the system metadata portion
1141
		String systemMetadataXML = ReplicationUtil.getSystemMetadataContent(docInfoStr);
1142
		docInfoStr = ReplicationUtil.getContentWithoutSystemMetadata(docInfoStr);
1143

    
1144
		docinfoParser.parse(new InputSource(new StringReader(docInfoStr)));
1145
		Hashtable<String, String> docinfoHash = dih.getDocInfo();
1146

    
1147
		return docinfoHash;
1148
	}
1149
	
1150
	/**
1151
	 * Gets a docInfo XML snippet for the replication API
1152
	 * @param docid
1153
	 * @return
1154
	 * @throws AccessControlException
1155
	 * @throws JiBXException
1156
	 * @throws IOException
1157
	 * @throws McdbException
1158
	 */
1159
	public static String getDocumentInfo(String docid) throws AccessControlException, JiBXException, IOException, McdbException {
1160
		StringBuffer sb = new StringBuffer();
1161

    
1162
		DocumentImpl doc = new DocumentImpl(docid);
1163
		sb.append("<documentinfo><docid>").append(docid);
1164
		sb.append("</docid>");
1165
		
1166
		try {
1167
			// serialize the System Metadata as XML for docinfo
1168
			String guid = IdentifierManager.getInstance().getGUID(doc.getDocID(), doc.getRev());
1169
			SystemMetadata systemMetadata = IdentifierManager.getInstance().getSystemMetadata(guid);
1170
			ByteArrayOutputStream baos = new ByteArrayOutputStream();
1171
			TypeMarshaller.marshalTypeToOutputStream(systemMetadata, baos);
1172
			String systemMetadataXML = baos.toString("UTF-8");
1173
			sb.append("<systemMetadata>");
1174
			sb.append(systemMetadataXML);
1175
			sb.append("</systemMetadata>");
1176
		} catch(McdbDocNotFoundException e) {
1177
		  logMetacat.warn("No SystemMetadata found for: " + docid);
1178
		}
1179
		
1180
		Calendar created = Calendar.getInstance();
1181
		created.setTime(doc.getCreateDate());
1182
		Calendar updated = Calendar.getInstance();
1183
		updated.setTime(doc.getUpdateDate());
1184
		
1185
		sb.append("<docname><![CDATA[").append(doc.getDocname());
1186
		sb.append("]]></docname><doctype>").append(doc.getDoctype());
1187
		sb.append("</doctype>");
1188
		sb.append("<user_owner>").append(doc.getUserowner());
1189
		sb.append("</user_owner><user_updated>").append(doc.getUserupdated());
1190
		sb.append("</user_updated>");
1191
		sb.append("<date_created>");
1192
		sb.append(DateTimeMarshaller.serializeDateToUTC(doc.getCreateDate()));
1193
		sb.append("</date_created>");
1194
		sb.append("<date_updated>");
1195
		sb.append(DateTimeMarshaller.serializeDateToUTC(doc.getUpdateDate()));
1196
		sb.append("</date_updated>");
1197
		sb.append("<home_server>");
1198
		sb.append(doc.getDocHomeServer());
1199
		sb.append("</home_server>");
1200
		sb.append("<public_access>").append(doc.getPublicaccess());
1201
		sb.append("</public_access><rev>").append(doc.getRev());
1202
		sb.append("</rev>");
1203

    
1204
		sb.append("<accessControl>");
1205

    
1206
		AccessControlForSingleFile acfsf = new AccessControlForSingleFile(docid); 
1207
		sb.append(acfsf.getAccessString());
1208
		
1209
		sb.append("</accessControl>");
1210

    
1211
		sb.append("</documentinfo>");
1212
			
1213
		return sb.toString();
1214
	}
1215
	
1216
	/**
1217
	 * Sends System Metadata as XML
1218
	 */
1219
	protected static void handleGetSystemMetadataRequest(
1220
			Hashtable<String, String[]> params, HttpServletResponse response) {
1221
		String guid = ((String[]) (params.get("guid")))[0];
1222
		String systemMetadataXML = null;
1223
		try {
1224
			
1225
			// serialize the System Metadata as XML 
1226
			SystemMetadata systemMetadata = IdentifierManager.getInstance().getSystemMetadata(guid);
1227
			ByteArrayOutputStream baos = new ByteArrayOutputStream();
1228
			TypeMarshaller.marshalTypeToOutputStream(systemMetadata, baos);
1229
			systemMetadataXML = baos.toString("UTF-8");
1230
				
1231
			// get a writer for sending back to response
1232
			response.setContentType("text/xml");
1233
			Writer out = response.getWriter();
1234
			out.write(systemMetadataXML);
1235
			out.close();
1236

    
1237
		} catch (Exception e) {
1238
			String msg = "ReplicationService.handleGetSystemMetadataRequest for guid: " + guid + " : " + e.getMessage();
1239
			logMetacat.error(msg);                         
1240
			logReplication.error(msg);
1241
		}
1242

    
1243
	}
1244
	
1245
	/**
1246
	 * when a forcereplication request comes in, local host sends a read request
1247
	 * to the requesting server (remote server) for the specified docid. Then
1248
	 * store it in local database.
1249
	 */
1250
	protected static void handleForceReplicateSystemMetadataRequest(
1251
			Hashtable<String, String[]> params, HttpServletResponse response,
1252
			HttpServletRequest request) {
1253
		String server = ((String[]) params.get("server"))[0]; // the server that
1254
		String guid = ((String[]) params.get("guid"))[0]; // sent the document
1255
		
1256
		try {
1257
			logReplication.info("ReplicationService.handleForceReplicateSystemMetadataRequest - Force replication system metadata request from: " + server);
1258
			// get the system metadata from server
1259
			URL docinfourl = new URL("https://" + server + "?server="
1260
					+ MetacatUtil.getLocalReplicationServerName()
1261
					+ "&action=getsystemmetadata&guid=" + guid);
1262
			
1263
			String systemMetadataXML = ReplicationService.getURLContent(docinfourl);
1264
						
1265
			// process system metadata
1266
			if (systemMetadataXML != null) {
1267
				SystemMetadata sysMeta = 
1268
					TypeMarshaller.unmarshalTypeFromStream(
1269
							SystemMetadata.class,
1270
							new ByteArrayInputStream(systemMetadataXML.getBytes("UTF-8")));
1271
				HazelcastService.getInstance().getSystemMetadataMap().put(sysMeta.getIdentifier(), sysMeta);
1272
				// submit for indexing
1273
                MetacatSolrIndex.getInstance().submit(sysMeta.getIdentifier(), sysMeta, null, true);
1274
			}
1275
      
1276
			logReplication.info("ReplicationService.handleForceReplicateSystemMetadataRequest - processed guid: " + guid);
1277
			EventLog.getInstance().log(request.getRemoteAddr(), request.getHeader("User-Agent"), REPLICATIONUSER, guid, "systemMetadata");
1278

    
1279
		} catch (Exception e) {
1280
			logMetacat.error("ReplicationService.handleForceReplicateRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG, e);                         
1281
			logReplication.error("ReplicationService.handleForceReplicateRequest - General error when processing guid: " + guid, e);
1282
		}
1283
	}
1284

    
1285
	/**
1286
	 * Sends a datafile to a remote host
1287
	 */
1288
	protected static void handleGetDataFileRequest(OutputStream outPut,
1289
			Hashtable<String, String[]> params, HttpServletResponse response)
1290

    
1291
	{
1292
		// File path for data file
1293
		String filepath;
1294
		// Request docid
1295
		String docId = ((String[]) (params.get("docid")))[0];
1296
		//check if the doicd is null
1297
		if (docId == null) {
1298
			logMetacat.error("ReplicationService.handleGetDataFileRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
1299
			logReplication.error("ReplicationService.handleGetDataFileRequest - Didn't specify docid for replication");
1300
			return;
1301
		}
1302

    
1303
		//try to open a https stream to test if the request server's public key
1304
		//in the key store, this is security issue
1305
		try {
1306
			filepath = PropertyService.getProperty("application.datafilepath");
1307
			String server = params.get("server")[0];
1308
			URL u = new URL("https://" + server + "?server="
1309
					+ MetacatUtil.getLocalReplicationServerName() + "&action=test");
1310
			String test = ReplicationService.getURLContent(u);
1311
			//couldn't pass the test
1312
			if (test.indexOf("successfully") == -1) {
1313
				//response.setContentType("text/xml");
1314
				//outPut.println("<error>Couldn't pass the trust test</error>");
1315
				logMetacat.error("ReplicationService.handleGetDataFileRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
1316
				logReplication.error("ReplicationService.handleGetDataFileRequest - Couldn't pass the trust test");
1317
				return;
1318
			}
1319
		}//try
1320
		catch (Exception ee) {
1321
			return;
1322
		}//catch
1323

    
1324
		if (!filepath.endsWith("/")) {
1325
			filepath += "/";
1326
		}
1327
		// Get file aboslute file name
1328
		String filename = filepath + docId;
1329

    
1330
		//MIME type
1331
		String contentType = null;
1332
		if (filename.endsWith(".xml")) {
1333
			contentType = "text/xml";
1334
		} else if (filename.endsWith(".css")) {
1335
			contentType = "text/css";
1336
		} else if (filename.endsWith(".dtd")) {
1337
			contentType = "text/plain";
1338
		} else if (filename.endsWith(".xsd")) {
1339
			contentType = "text/xml";
1340
		} else if (filename.endsWith("/")) {
1341
			contentType = "text/html";
1342
		} else {
1343
			File f = new File(filename);
1344
			if (f.isDirectory()) {
1345
				contentType = "text/html";
1346
			} else {
1347
				contentType = "application/octet-stream";
1348
			}
1349
		}
1350

    
1351
		// Set the mime type
1352
		response.setContentType(contentType);
1353

    
1354
		// Get the content of the file
1355
		FileInputStream fin = null;
1356
		try {
1357
			// FileInputStream to metacat
1358
			fin = new FileInputStream(filename);
1359
			// 4K buffer
1360
			byte[] buf = new byte[4 * 1024];
1361
			// Read data from file input stream to byte array
1362
			int b = fin.read(buf);
1363
			// Write to outStream from byte array
1364
			while (b != -1) {
1365
				outPut.write(buf, 0, b);
1366
				b = fin.read(buf);
1367
			}
1368
			// close file input stream
1369
			fin.close();
1370

    
1371
		} catch (Exception e) {
1372
			logMetacat.error("ReplicationService.handleGetDataFileRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
1373
			logReplication.error("ReplicationService.handleGetDataFileRequest - error getting data file from MetacatReplication."
1374
					+ "handlGetDataFileRequest " + e.getMessage());
1375
			e.printStackTrace(System.out);
1376
		} finally {
1377
		    IOUtils.closeQuietly(fin);
1378
		}
1379

    
1380
	}
1381

    
1382
	/**
1383
	 * Sends a document to a remote host
1384
	 */
1385
	protected static void handleGetDocumentRequest(
1386
			Hashtable<String, String[]> params, HttpServletResponse response) {
1387

    
1388
		String urlString = null;
1389
		String documentPath = null;
1390
		String errorMsg = null;
1391
		FileOutputStream fos = null;
1392
		InputStream is = null;
1393
		OutputStream outputStream = null;
1394
		try {
1395
			// try to open a https stream to test if the request server's public
1396
			// key
1397
			// in the key store, this is security issue
1398
			String server = params.get("server")[0];
1399
			urlString = "https://" + server + "?server="
1400
					+ MetacatUtil.getLocalReplicationServerName() + "&action=test";
1401
			URL u = new URL(urlString);
1402
			String test = ReplicationService.getURLContent(u);
1403
			// couldn't pass the test
1404
			if (test.indexOf("successfully") == -1) {
1405
				response.setContentType("text/xml");
1406
				Writer out = response.getWriter();
1407
				out.write("<error>Couldn't pass the trust test " + test + " </error>");
1408
				out.close();
1409
				return;
1410
			}
1411

    
1412
			String docid = params.get("docid")[0];
1413
			logReplication.debug("ReplicationService.handleGetDocumentRequest - MetacatReplication.handleGetDocumentRequest for docid: "
1414
					+ docid);
1415
			DocumentImpl di = new DocumentImpl(docid);
1416

    
1417
			String documentDir = PropertyService
1418
					.getProperty("application.documentfilepath");
1419
			documentPath = documentDir + FileUtil.getFS() + docid;
1420

    
1421
			// if the document does not exist on disk, read it from db and write
1422
			// it to disk.
1423
			if (FileUtil.getFileStatus(documentPath) == FileUtil.DOES_NOT_EXIST
1424
					|| FileUtil.getFileSize(documentPath) == 0) {
1425
				fos = new FileOutputStream(documentPath);
1426
				is = di.toXml(fos, null, null, true);
1427
				fos.close();
1428
				is.close();
1429
			}
1430

    
1431
			// read the file from disk and send it to outputstream
1432
			outputStream = response.getOutputStream();
1433
			is = di.readFromFileSystem(outputStream, null, null, documentPath);
1434
			is.close();
1435
			outputStream.close();
1436

    
1437
			logReplication.info("ReplicationService.handleGetDocumentRequest - document " + docid + " sent");
1438

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

    
1507
	}
1508

    
1509
	/**
1510
	 * Sends a list of all of the documents on this sever along with their
1511
	 * revision numbers. The format is: <!ELEMENT replication (server, updates)>
1512
	 * <!ELEMENT server (#PCDATA)> <!ELEMENT updates ((updatedDocument |
1513
	 * deleteDocument | revisionDocument)*)> <!ELEMENT updatedDocument (docid,
1514
	 * rev, datafile*)> <!ELEMENT deletedDocument (docid, rev)> <!ELEMENT
1515
	 * revisionDocument (docid, rev, datafile*)> <!ELEMENT docid (#PCDATA)>
1516
	 * <!ELEMENT rev (#PCDATA)> <!ELEMENT datafile (#PCDATA)> note that the rev
1517
	 * in deletedDocument is always empty. I just left it in there to make the
1518
	 * parser implementation easier.
1519
	 */
1520
	protected static void handleUpdateRequest(Hashtable<String, String[]> params,
1521
			HttpServletResponse response) {
1522
		// Checked out DBConnection
1523
		DBConnection dbConn = null;
1524
		// DBConenction serial number when checked it out
1525
		int serialNumber = -1;
1526
		PreparedStatement pstmt = null;
1527
		// Server list to store server info of xml_replication table
1528
		ReplicationServerList serverList = null;
1529
		
1530
		// a writer for response
1531
		Writer out = null;
1532

    
1533
		try {
1534
			// get writer, TODO: encoding?
1535
			response.setContentType("text/xml");
1536
			out = response.getWriter();
1537
			
1538
			// Check out a DBConnection from pool
1539
			dbConn = DBConnectionPool
1540
					.getDBConnection("MetacatReplication.handleUpdateRequest");
1541
			serialNumber = dbConn.getCheckOutSerialNumber();
1542
			// Create a server list from xml_replication table
1543
			serverList = new ReplicationServerList();
1544

    
1545
			// Get remote server name from param
1546
			String server = ((String[]) params.get("server"))[0];
1547
			// If no servr name in param, return a error
1548
			if (server == null || server.equals("")) {
1549
				out.write("<error>Request didn't specify server name</error>");
1550
				out.close();
1551
				return;
1552
			}//if
1553

    
1554
			//try to open a https stream to test if the request server's public key
1555
			//in the key store, this is security issue
1556
			String testUrl = "https://" + server + "?server="
1557
            + MetacatUtil.getLocalReplicationServerName() + "&action=test";
1558
			logReplication.info("Running trust test: " + testUrl);
1559
			URL u = new URL(testUrl);
1560
			String test = ReplicationService.getURLContent(u);
1561
			logReplication.info("Ouput from test is '" + test + "'");
1562
			//couldn't pass the test
1563
			if (test.indexOf("successfully") == -1) {
1564
			    logReplication.error("Trust test failed.");
1565
				out.write("<error>Couldn't pass the trust test</error>");
1566
				out.close();
1567
				return;
1568
			}
1569
			logReplication.info("Trust test succeeded.");
1570

    
1571
			// Check if local host configure to replicate xml documents to remote
1572
			// server. If not send back a error message
1573
			if (!serverList.getReplicationValue(server)) {
1574
				out.write("<error>Configuration not allow to replicate document to you</error>");
1575
				out.close();
1576
				return;
1577
			}//if
1578

    
1579
			// Store the sql command
1580
			StringBuffer docsql = new StringBuffer();
1581
			StringBuffer revisionSql = new StringBuffer();
1582
			
1583
			// Store the data set file
1584
			Vector<Vector<String>> packageFiles = new Vector<Vector<String>>();
1585

    
1586
			// Append local server's name and replication servlet to doclist
1587
			out.append("<?xml version=\"1.0\"?><replication>");
1588
			out.append("<server>")
1589
					.append(MetacatUtil.getLocalReplicationServerName());
1590
			//doclist.append(util.getProperty("replicationpath"));
1591
			out.append("</server><updates>");
1592

    
1593
			// Get correct docid that reside on this server according the requesting
1594
			// server's replicate and data replicate value in xml_replication table
1595
			docsql.append(DatabaseService.getInstance().getDBAdapter().getReplicationDocumentListSQL());
1596
			//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)) ");
1597
			revisionSql.append("select docid, rev, doctype from xml_revisions ");
1598
			// If the localhost is not a hub to the remote server, only replicate
1599
			// the docid' which home server is local host (server_location =1)
1600
			if (!serverList.getHubValue(server)) {
1601
				String serverLocationDoc = " and a.server_location = 1";
1602
				String serverLocationRev = "where server_location = 1";
1603
				docsql.append(serverLocationDoc);
1604
				revisionSql.append(serverLocationRev);
1605
			}
1606
			logReplication.info("ReplicationService.handleUpdateRequest - Doc sql: " + docsql.toString());
1607

    
1608
			// Get any deleted documents
1609
			StringBuffer delsql = new StringBuffer();
1610
			delsql.append("SELECT t1.docid FROM xml_revisions t1 LEFT JOIN xml_documents t2 on t1.docid = t2.docid WHERE t2.docid IS NULL "); 
1611
			
1612
			// If the localhost is not a hub to the remote server, only replicate
1613
			// the docid' which home server is local host (server_location =1)
1614
			if (!serverList.getHubValue(server)) {
1615
				delsql.append("and t1.server_location = 1");
1616
			}
1617
			logReplication.info("ReplicationService.handleUpdateRequest - Deleted sql: " + delsql.toString());
1618

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

    
1696
			pstmt = dbConn.prepareStatement(delsql.toString());
1697
			//usage count should increas 1
1698
			dbConn.increaseUsageCount(1);
1699

    
1700
			pstmt.execute();
1701
			rs = pstmt.getResultSet();
1702
			tablehasrows = rs.next();
1703
			while (tablehasrows) { //handle the deleted documents
1704
				out.append("<deletedDocument><docid>").append(rs.getString(1));
1705
				out.append("</docid><rev></rev></deletedDocument>");
1706
				//note that rev is always empty for deleted docs
1707
				tablehasrows = rs.next();
1708
			}
1709

    
1710
			//now we can put the package files into the xml results
1711
			for (int i = 0; i < packageFiles.size(); i++) {
1712
				Vector<String> v = packageFiles.elementAt(i);
1713
				out.append("<updatedDocument>");
1714
				out.append("<docid>").append(v.elementAt(0));
1715
				out.append("</docid><rev>");
1716
				out.append(v.elementAt(1));
1717
				out.append("</rev>");
1718
				out.append("</updatedDocument>");
1719
			}
1720
			// add revision doc list  
1721
			out.append(prepareRevisionDoc(dbConn, revisionSql.toString(),
1722
					replicateData));
1723

    
1724
			out.append("</updates></replication>");
1725
			logReplication.info("ReplicationService.handleUpdateRequest - done writing to output stream.");
1726
			pstmt.close();
1727
			//conn.close();
1728

    
1729
		} catch (Exception e) {
1730
			logMetacat.error("ReplicationService.handleUpdateRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
1731
			logReplication.error("ReplicationService.handleUpdateRequest - error in MetacatReplication." + "handleupdaterequest: "
1732
					+ e.getMessage());
1733
			//e.printStackTrace(System.out);
1734
			try {
1735
				out.write("<error>" + e.getMessage() + "</error>");
1736
			} catch (IOException e1) {
1737
				logMetacat.error(e1.getMessage(), e1);
1738
			}
1739
		} finally {
1740
			try {
1741
				pstmt.close();
1742
			}//try
1743
			catch (SQLException ee) {
1744
				logMetacat.error("ReplicationService.handleUpdateRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
1745
				logReplication.error("ReplicationService.handleUpdateRequest - Error in MetacatReplication."
1746
						+ "handleUpdaterequest to close pstmt: " + ee.getMessage());
1747
			}//catch
1748
			finally {
1749
				DBConnectionPool.returnDBConnection(dbConn, serialNumber);
1750
			}//finally
1751
			try {
1752
				out.close();
1753
			} catch (IOException e) {
1754
				logMetacat.error(e.getMessage(), e);
1755
			}
1756
		}//finally
1757

    
1758
	}//handlUpdateRequest
1759

    
1760
	/**
1761
	 * 
1762
	 * @param dbConn connection for doing the update
1763
	 * @param docid the document id to update
1764
	 * @param owner the user_owner
1765
	 * @param updater the user_updated
1766
	 * @throws SQLException
1767
	 */
1768
	public static void updateUserOwner(DBConnection dbConn, String docid, String owner, String updater) throws SQLException {
1769
	
1770
		String sql = 
1771
			"UPDATE xml_documents " +
1772
			"SET user_owner = ?, " +
1773
			"user_updated = ? " +
1774
			"WHERE docid = ?;";
1775
		PreparedStatement pstmt = dbConn.prepareStatement(sql);
1776
		//usage count should increas 1
1777
		dbConn.increaseUsageCount(1);
1778

    
1779
		docid = DocumentUtil.getSmartDocId(docid);
1780
		pstmt.setString(1, owner);
1781
		pstmt.setString(2, updater);
1782
		pstmt.setString(3, docid);
1783
		pstmt.execute();
1784
		pstmt.close();
1785
		
1786
		dbConn.commit();
1787
	}
1788
	
1789
	/*
1790
	 * This method will get the xml string for document in xml_revision
1791
	 * The schema look like <!ELEMENT revisionDocument (docid, rev, datafile*)>
1792
	 */
1793
	private static String prepareRevisionDoc(DBConnection dbConn, String revSql,
1794
			boolean replicateData) throws Exception {
1795
		logReplication.warn("ReplicationService.prepareRevisionDoc - The revision document sql is " + revSql);
1796
		StringBuffer revDocList = new StringBuffer();
1797
		PreparedStatement pstmt = dbConn.prepareStatement(revSql);
1798
		//usage count should increas 1
1799
		dbConn.increaseUsageCount(1);
1800

    
1801
		pstmt.execute();
1802
		ResultSet rs = pstmt.getResultSet();
1803
		logReplication.warn("Processing replication revision for documents");
1804
		while (rs.next()) {
1805
			String recordDoctype = rs.getString(3);
1806

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

    
1811
				// do nothing
1812
			} else {
1813
				String docid = rs.getString(1);
1814
				int rev = rs.getInt(2);
1815
				logMetacat.debug("Processing replication revision for docid: " + docid + "." + rev);
1816

    
1817
				revDocList.append("<revisionDocument>");
1818
				revDocList.append("<docid>").append(docid);
1819
				revDocList.append("</docid><rev>").append(rev);
1820
				revDocList.append("</rev>");
1821
				// data file
1822
				if (recordDoctype.equals("BIN")) {
1823
					revDocList.append("<datafile>");
1824
					revDocList.append(DATA_FILE_FLAG);
1825
					revDocList.append("</datafile>");
1826
				}
1827
				revDocList.append("</revisionDocument>");
1828

    
1829
			}//else
1830
		}
1831
		//System.out.println("The revision list is"+ revDocList.toString());
1832
		return revDocList.toString();
1833
	}
1834

    
1835
	/**
1836
	 * Returns the xml_catalog table encoded in xml
1837
	 */
1838
	public static String getCatalogXML() {
1839
		return handleGetCatalogRequest(null, null, false);
1840
	}
1841

    
1842
	/**
1843
	 * Sends the contents of the xml_catalog table encoded in xml
1844
	 * The xml format is:
1845
	 * <!ELEMENT xml_catalog (row*)>
1846
	 * <!ELEMENT row (entry_type, source_doctype, target_doctype, public_id,
1847
	 *                system_id)>
1848
	 * All of the sub elements of row are #PCDATA
1849

    
1850
	 * If printFlag == false then do not print to out.
1851
	 */
1852
	protected static String handleGetCatalogRequest(
1853
			Hashtable<String, String[]> params, HttpServletResponse response,
1854
			boolean printFlag) {
1855
		DBConnection dbConn = null;
1856
		int serialNumber = -1;
1857
		PreparedStatement pstmt = null;
1858
		Writer out = null;
1859
		try {
1860
			// get writer, TODO: encoding?
1861
		    if(printFlag)
1862
		    {
1863
		        response.setContentType("text/xml");
1864
		        out = response.getWriter();
1865
		    }
1866
			/*conn = MetacatReplication.getDBConnection("MetacatReplication." +
1867
			                                          "handleGetCatalogRequest");*/
1868
			dbConn = DBConnectionPool
1869
					.getDBConnection("MetacatReplication.handleGetCatalogRequest");
1870
			serialNumber = dbConn.getCheckOutSerialNumber();
1871
			pstmt = dbConn.prepareStatement("select entry_type, "
1872
					+ "source_doctype, target_doctype, public_id, "
1873
					+ "system_id from xml_catalog");
1874
			pstmt.execute();
1875
			ResultSet rs = pstmt.getResultSet();
1876
			boolean tablehasrows = rs.next();
1877
			StringBuffer sb = new StringBuffer();
1878
			sb.append("<?xml version=\"1.0\"?><xml_catalog>");
1879
			while (tablehasrows) {
1880
				sb.append("<row><entry_type>").append(rs.getString(1));
1881
				sb.append("</entry_type><source_doctype>").append(rs.getString(2));
1882
				sb.append("</source_doctype><target_doctype>").append(rs.getString(3));
1883
				sb.append("</target_doctype><public_id>").append(rs.getString(4));
1884
				// system id may not have server url on front.  Add it if not.
1885
				String systemID = rs.getString(5);
1886
				if (!systemID.startsWith("http://")) {
1887
					systemID = SystemUtil.getContextURL() + systemID;
1888
				}
1889
				sb.append("</public_id><system_id>").append(systemID);
1890
				sb.append("</system_id></row>");
1891

    
1892
				tablehasrows = rs.next();
1893
			}
1894
			sb.append("</xml_catalog>");
1895
			//conn.close();
1896
			if (printFlag) {
1897
				response.setContentType("text/xml");
1898
				out.write(sb.toString());
1899
			}
1900
			pstmt.close();
1901
			return sb.toString();
1902
		} catch (Exception e) {
1903
			logMetacat.error("ReplicationService.handleGetCatalogRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
1904
			logReplication.error("ReplicationService.handleGetCatalogRequest - error in MetacatReplication.handleGetCatalogRequest:"
1905
					+ e.getMessage());
1906
			e.printStackTrace(System.out);
1907
			if (printFlag) {
1908
				try {
1909
					out.write("<error>" + e.getMessage() + "</error>");
1910
				} catch (IOException e1) {
1911
					logMetacat.error(e1.getMessage(), e1);
1912
				}
1913
			}
1914
		} finally {
1915
			try {
1916
				pstmt.close();
1917
			}//try
1918
			catch (SQLException ee) {
1919
				logMetacat.error("ReplicationService.handleGetCatalogRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
1920
				logReplication.error("ReplicationService.handleGetCatalogRequest - Error in MetacatReplication.handleGetCatalogRequest: "
1921
						+ ee.getMessage());
1922
			}//catch
1923
			finally {
1924
				DBConnectionPool.returnDBConnection(dbConn, serialNumber);
1925
			}//finally
1926
			if (out != null) {
1927
				try {
1928
					out.close();
1929
				} catch (IOException e1) {
1930
					logMetacat.error(e1.getMessage(), e1);
1931
				}
1932
			}
1933
		}//finally
1934

    
1935
		return null;
1936
	}
1937

    
1938
	/**
1939
	 * Sends the current system date to the remote server.  Using this action
1940
	 * for replication gets rid of any problems with syncronizing clocks
1941
	 * because a time specific to a document is always kept on its home server.
1942
	 */
1943
	protected static void handleGetTimeRequest(
1944
			Hashtable<String, String[]> params, HttpServletResponse response) {
1945
		
1946
		// use standard format -- the receiving end wants this too
1947
		String dateString = DateTimeMarshaller.serializeDateToUTC(Calendar.getInstance().getTime());
1948
		
1949
		// get a writer for sending back to response
1950
		response.setContentType("text/xml");
1951
		Writer out = null;
1952
		try {
1953
			out = response.getWriter();
1954
			out.write("<timestamp>" + dateString + "</timestamp>");
1955
			out.close();
1956
		} catch (IOException e) {
1957
			logMetacat.error(e.getMessage(), e);
1958
		}
1959
		
1960
	}
1961

    
1962
	/**
1963
	 * this method handles the timeout for a file lock.  when a lock is
1964
	 * granted it is granted for 30 seconds.  When this thread runs out
1965
	 * it deletes the docid from the queue, thus eliminating the lock.
1966
	 */
1967
	public void run() {
1968
		try {
1969
			logReplication.info("ReplicationService.run - thread started for docid: "
1970
					+ (String) fileLocks.elementAt(0));
1971

    
1972
			Thread.sleep(30000); //the lock will expire in 30 seconds
1973
			logReplication.info("thread for docid: "
1974
					+ (String) fileLocks.elementAt(fileLocks.size() - 1) + " exiting.");
1975

    
1976
			fileLocks.remove(fileLocks.size() - 1);
1977
			//fileLocks is treated as a FIFO queue.  If there are more than one lock
1978
			//in the vector, the first one inserted will be removed.
1979
		} catch (Exception e) {
1980
			logMetacat.error("ReplicationService.run - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
1981
			logReplication.error("ReplicationService.run - error in file lock thread from "
1982
					+ "MetacatReplication.run: " + e.getMessage());
1983
		}
1984
	}
1985

    
1986
	/**
1987
	 * Returns the name of a server given a serverCode
1988
	 * @param serverCode the serverid of the server
1989
	 * @return the servername or null if the specified serverCode does not
1990
	 *         exist.
1991
	 */
1992
	public static String getServerNameForServerCode(int serverCode) {
1993
		//System.out.println("serverid: " + serverCode);
1994
		DBConnection dbConn = null;
1995
		int serialNumber = -1;
1996
		PreparedStatement pstmt = null;
1997
		try {
1998
			dbConn = DBConnectionPool.getDBConnection("MetacatReplication.getServer");
1999
			serialNumber = dbConn.getCheckOutSerialNumber();
2000
			String sql = new String("select server from "
2001
					+ "xml_replication where serverid = ?");
2002
			pstmt = dbConn.prepareStatement(sql);
2003
			pstmt.setInt(1, serverCode);
2004
			//System.out.println("getserver sql: " + sql);
2005
			pstmt.execute();
2006
			ResultSet rs = pstmt.getResultSet();
2007
			boolean tablehasrows = rs.next();
2008
			if (tablehasrows) {
2009
				//System.out.println("server: " + rs.getString(1));
2010
				return rs.getString(1);
2011
			}
2012

    
2013
			//conn.close();
2014
		} catch (Exception e) {
2015
			logMetacat.error("ReplicationService.getServerNameForServerCode - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
2016
			logReplication.error("ReplicationService.getServerNameForServerCode - Error in MetacatReplication.getServer: " + e.getMessage());
2017
		} finally {
2018
			try {
2019
				pstmt.close();
2020
			}//try
2021
			catch (SQLException ee) {
2022
				logMetacat.error("ReplicationService.getServerNameForServerCode - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
2023
				logReplication.error("ReplicationService.getServerNameForServerCode - Error in MetacactReplication.getserver: "
2024
						+ ee.getMessage());
2025
			}//catch
2026
			finally {
2027
				DBConnectionPool.returnDBConnection(dbConn, serialNumber);
2028
			}//fianlly
2029
		}//finally
2030

    
2031
		return null;
2032
		//return null if the server does not exist
2033
	}
2034

    
2035
	/**
2036
	 * Returns a server code given a server name
2037
	 * @param server the name of the server
2038
	 * @return integer > 0 representing the code of the server, 0 if the server
2039
	 *  does not exist.
2040
	 */
2041
	public static int getServerCodeForServerName(String server) throws ServiceException {
2042
		DBConnection dbConn = null;
2043
		int serialNumber = -1;
2044
		PreparedStatement pstmt = null;
2045
		int serverCode = 0;
2046

    
2047
		try {
2048

    
2049
			//conn = util.openDBConnection();
2050
			dbConn = DBConnectionPool.getDBConnection("MetacatReplication.getServerCode");
2051
			serialNumber = dbConn.getCheckOutSerialNumber();
2052
			pstmt = dbConn.prepareStatement("SELECT serverid FROM xml_replication "
2053
					+ "WHERE server LIKE ?");
2054
			pstmt.setString(1, server);
2055
			pstmt.execute();
2056
			ResultSet rs = pstmt.getResultSet();
2057
			boolean tablehasrows = rs.next();
2058
			if (tablehasrows) {
2059
				serverCode = rs.getInt(1);
2060
				pstmt.close();
2061
				//conn.close();
2062
				return serverCode;
2063
			}
2064

    
2065
		} catch (SQLException sqle) {
2066
			throw new ServiceException("ReplicationService.getServerCodeForServerName - " 
2067
					+ "SQL error when getting server code: " + sqle.getMessage());
2068

    
2069
		} finally {
2070
			try {
2071
				pstmt.close();
2072
				//conn.close();
2073
			}//try
2074
			catch (Exception ee) {
2075
				logMetacat.error("ReplicationService.getServerCodeForServerName - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
2076
				logReplication.error("ReplicationService.getServerNameForServerCode - Error in MetacatReplicatio.getServerCode: "
2077
						+ ee.getMessage());
2078

    
2079
			}//catch
2080
			finally {
2081
				DBConnectionPool.returnDBConnection(dbConn, serialNumber);
2082
			}//finally
2083
		}//finally
2084

    
2085
		return serverCode;
2086
	}
2087
	
2088
	/**
2089
	 * Returns a Map of serverCode=serverName 
2090
	 * @return Map of server codes to names (URIs)
2091
	 */
2092
	public static Map<Integer, String> getServerCodes() throws ServiceException {
2093
		DBConnection dbConn = null;
2094
		int serialNumber = -1;
2095
		PreparedStatement pstmt = null;
2096
		
2097
		Map<Integer, String> codes = new HashMap<Integer, String>();
2098

    
2099
		try {
2100

    
2101
			dbConn = DBConnectionPool.getDBConnection("MetacatReplication.getServerCodes");
2102
			serialNumber = dbConn.getCheckOutSerialNumber();
2103
			pstmt = dbConn.prepareStatement("SELECT serverid, server FROM xml_replication ");
2104
			pstmt.execute();
2105
			ResultSet rs = pstmt.getResultSet();
2106
			while (rs.next()) {
2107
				int serverCode = rs.getInt(1);
2108
				String server = rs.getString(2);
2109
				codes.put(serverCode, server);
2110
			}
2111
			pstmt.close();
2112
			
2113
		} catch (SQLException sqle) {
2114
			throw new ServiceException("ReplicationService.getServerCodes - " 
2115
					+ "SQL error when getting server map: " + sqle.getMessage());
2116

    
2117
		} finally {
2118
			try {
2119
				pstmt.close();
2120
			}//try
2121
			catch (Exception ee) {
2122
				logMetacat.error("ReplicationService.getServerCodes - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
2123
				logReplication.error("ReplicationService.getServerCodes - Error in MetacatReplicatio.getServerCodes: "
2124
						+ ee.getMessage());
2125

    
2126
			}//catch
2127
			finally {
2128
				DBConnectionPool.returnDBConnection(dbConn, serialNumber);
2129
			}//finally
2130
		}//finally
2131

    
2132
		return codes;
2133
	}
2134

    
2135
	/**
2136
	 * Method to get a host server information for given docid
2137
	 * @param conn a connection to the database
2138
	 */
2139
	public static Hashtable<String, String> getHomeServerInfoForDocId(String docId) {
2140
		Hashtable<String, String> sl = new Hashtable<String, String>();
2141
		DBConnection dbConn = null;
2142
		int serialNumber = -1;
2143
		docId = DocumentUtil.getDocIdFromString(docId);
2144
		PreparedStatement pstmt = null;
2145
		int serverLocation;
2146
		try {
2147
			//get conection
2148
			dbConn = DBConnectionPool.getDBConnection("ReplicationHandler.getHomeServer");
2149
			serialNumber = dbConn.getCheckOutSerialNumber();
2150
			//get a server location from xml_document table
2151
			pstmt = dbConn.prepareStatement("select server_location from xml_documents "
2152
					+ "where docid = ?");
2153
			pstmt.setString(1, docId);
2154
			pstmt.execute();
2155
			ResultSet serverName = pstmt.getResultSet();
2156
			//get a server location
2157
			if (serverName.next()) {
2158
				serverLocation = serverName.getInt(1);
2159
				pstmt.close();
2160
			} else {
2161
				pstmt.close();
2162
				//ut.returnConnection(conn);
2163
				return null;
2164
			}
2165
			pstmt = dbConn.prepareStatement("select server, last_checked, replicate "
2166
					+ "from xml_replication where serverid = ?");
2167
			//increase usage count
2168
			dbConn.increaseUsageCount(1);
2169
			pstmt.setInt(1, serverLocation);
2170
			pstmt.execute();
2171
			ResultSet rs = pstmt.getResultSet();
2172
			boolean tableHasRows = rs.next();
2173
			if (tableHasRows) {
2174

    
2175
				String server = rs.getString(1);
2176
				String last_checked = rs.getString(2);
2177
				if (!server.equals("localhost")) {
2178
					sl.put(server, last_checked);
2179
				}
2180

    
2181
			} else {
2182
				pstmt.close();
2183
				//ut.returnConnection(conn);
2184
				return null;
2185
			}
2186
			pstmt.close();
2187
		} catch (Exception e) {
2188
			logMetacat.error("ReplicationService.getHomeServerInfoForDocId - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
2189
			logReplication.error("ReplicationService.getHomeServerInfoForDocId - error in replicationHandler.getHomeServer(): "
2190
					+ e.getMessage());
2191
		} finally {
2192
			try {
2193
				pstmt.close();
2194
				//ut.returnConnection(conn);
2195
			} catch (Exception ee) {
2196
				logMetacat.error("ReplicationService.getHomeServerInfoForDocId - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
2197
				logReplication.error("ReplicationService.getHomeServerInfoForDocId - Eror irn rplicationHandler.getHomeServer() "
2198
						+ "to close pstmt: " + ee.getMessage());
2199
			} finally {
2200
				DBConnectionPool.returnDBConnection(dbConn, serialNumber);
2201
			}
2202

    
2203
		}//finally
2204
		return sl;
2205
	}
2206

    
2207
	/**
2208
	 * Returns a home server location  given a accnum
2209
	 * @param accNum , given accNum for a document
2210
	 *
2211
	 */
2212
	public static int getHomeServerCodeForDocId(String accNum) throws ServiceException {
2213
		DBConnection dbConn = null;
2214
		int serialNumber = -1;
2215
		PreparedStatement pstmt = null;
2216
		int serverCode = 1;
2217
		String docId = DocumentUtil.getDocIdFromString(accNum);
2218

    
2219
		try {
2220

    
2221
			// Get DBConnection
2222
			dbConn = DBConnectionPool
2223
					.getDBConnection("ReplicationHandler.getServerLocation");
2224
			serialNumber = dbConn.getCheckOutSerialNumber();
2225
			pstmt = dbConn.prepareStatement("SELECT server_location FROM xml_documents "
2226
					+ "WHERE docid LIKE ? ");
2227
			pstmt.setString(1, docId);
2228
			pstmt.execute();
2229
			ResultSet rs = pstmt.getResultSet();
2230
			boolean tablehasrows = rs.next();
2231
			//If a document is find, return the server location for it
2232
			if (tablehasrows) {
2233
				serverCode = rs.getInt(1);
2234
				pstmt.close();
2235
				//conn.close();
2236
				return serverCode;
2237
			}
2238
			//if couldn't find in xml_documents table, we think server code is 1
2239
			//(this is new document)
2240
			else {
2241
				pstmt.close();
2242
				//conn.close();
2243
				return serverCode;
2244
			}
2245

    
2246
		} catch (SQLException sqle) {
2247
			throw new ServiceException("ReplicationService.getHomeServerCodeForDocId - " 
2248
					+ "SQL error when getting home server code for docid: " + docId + " : " 
2249
					+ sqle.getMessage());
2250

    
2251
		} finally {
2252
			try {
2253
				pstmt.close();
2254
				//conn.close();
2255

    
2256
			} catch (SQLException sqle) {
2257
				logMetacat.error("ReplicationService.getHomeServerCodeForDocId - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
2258
				logReplication.error("ReplicationService.getHomeServerCodeForDocId - ReplicationService.getHomeServerCodeForDocId - " 
2259
						+ "SQL error when getting home server code for docid: " + docId + " : " 
2260
						+ sqle.getMessage());
2261
			} finally {
2262
				DBConnectionPool.returnDBConnection(dbConn, serialNumber);
2263
			}//finally
2264
		}//finally
2265
		//return serverCode;
2266
	}
2267

    
2268
	/**
2269
	 * This method returns the content of a url
2270
	 * @param u the url to return the content from
2271
	 * @return a string representing the content of the url
2272
	 * @throws java.io.IOException
2273
	 */
2274
	public static String getURLContent(URL u) throws Exception {
2275
		char istreamChar;
2276
		int istreamInt;
2277
		// get the response content
2278
		InputStream input = getURLStream(u);
2279
		logReplication.info("ReplicationService.getURLContent - After getting response from: " + u.toString());
2280
		String content = IOUtils.toString(input, "UTF-8");
2281
        return content;
2282
	}
2283
	
2284
	/**
2285
	 * This method returns the InputStream after opening a url
2286
	 * @param u the url to return the content from
2287
	 * @return a InputStream representing the content of the url
2288
	 * @throws java.io.IOException
2289
	 */
2290
	public static InputStream getURLStream(URL u) throws Exception {
2291
	    logReplication.info("Getting url stream from " + u.toString());
2292
		logReplication.info("ReplicationService.getURLStream - Before sending request to: " + u.toString());
2293
		// use httpclient to set up SSL
2294
		HttpMultipartRestClient client = getSSLClient();
2295
		// get the response content
2296
		InputStream input = client.doGetRequest(u.toString(), CLIENTTIMEOUT);
2297
		logReplication.info("ReplicationService.getURLStream - After getting response from: " + u.toString());
2298
		
2299
		return input;		
2300
	}
2301
	
2302
	/**
2303
     * This method returns a byte array after opening a url
2304
     * @param u the url to return the content from
2305
     * @return a InputStream representing the content of the url
2306
     * @throws java.io.IOException
2307
     */
2308
    public static byte[] getURLBytes(URL u) throws Exception {
2309
        InputStream input = getURLStream(u);
2310
        byte[] bytes = IOUtils.toByteArray(input);
2311
        return bytes;       
2312
    }
2313
	
2314
	/**
2315
	 * Sets up an HttpClient with SSL connection.
2316
	 * Sends client certificate to the server when doing the request.
2317
	 * @return
2318
	 */
2319
	private static HttpMultipartRestClient getSSLClient() {
2320
		HttpMultipartRestClient client = new HttpMultipartRestClient();
2321
		
2322
		// set up this server's client identity
2323
		String subject = null;
2324
		try {
2325
			// TODO: should there be alternative ways to get the key and certificate?
2326
			String certificateFile = PropertyService.getProperty("replication.certificate.file");
2327
	    	String keyFile = PropertyService.getProperty("replication.privatekey.file");
2328
			String keyPassword = PropertyService.getProperty("replication.privatekey.password");
2329
			X509Certificate certificate = CertificateManager.getInstance().loadCertificateFromFile(certificateFile);
2330
			PrivateKey privateKey = CertificateManager.getInstance().loadPrivateKeyFromFile(keyFile, keyPassword);
2331
			subject = CertificateManager.getInstance().getSubjectDN(certificate);
2332
			CertificateManager.getInstance().registerCertificate(subject, certificate, privateKey);
2333
		} catch (Exception e) {
2334
			// this is pretty much required for replication communication
2335
			logReplication.warn("Could not find server's client certificate/private key: " + e.getMessage());
2336
		}
2337
		
2338
		// set the configured timeout
2339
		//client.setTimeouts(CLIENTTIMEOUT);
2340

    
2341
		SSLSocketFactory socketFactory = null;
2342
		try {
2343
			socketFactory = CertificateManager.getInstance().getSSLSocketFactory(subject);
2344
		} catch (FileNotFoundException e) {
2345
			// these are somewhat expected for anonymous client use
2346
			logReplication.warn("Could not set up SSL connection for client - likely because the certificate could not be located: " + e.getMessage());
2347
		} catch (Exception e) {
2348
			// this is likely more severe
2349
			logReplication.warn("Funky SSL going on: " + e.getClass() + ":: " + e.getMessage());
2350
		}
2351
		try {
2352
			//443 is the default port, this value is overridden if explicitly set in the URL
2353
			Scheme sch = new Scheme("https", 443, socketFactory);
2354
			client.getHttpClient().getConnectionManager().getSchemeRegistry().register(sch);
2355
		} catch (Exception e) {
2356
			// this is likely more severe
2357
			logReplication.error("Failed to set up SSL connection for client. Continuing. " + e.getClass() + ":: " + e.getMessage(), e);
2358
		}
2359
		return client;
2360
	}
2361
	
2362

    
2363
//	/**
2364
//	 * Method for writing replication messages to a log file specified in
2365
//	 * metacat.properties
2366
//	 */
2367
//	public static void replLog(String message) {
2368
//		try {
2369
//			FileOutputStream fos = new FileOutputStream(PropertyService
2370
//					.getProperty("replication.logdir")
2371
//					+ "/metacatreplication.log", true);
2372
//			PrintWriter pw = new PrintWriter(fos);
2373
//			SimpleDateFormat formatter = new SimpleDateFormat("yy-MM-dd HH:mm:ss");
2374
//			java.util.Date localtime = new java.util.Date();
2375
//			String dateString = formatter.format(localtime);
2376
//			dateString += " :: " + message;
2377
//			// time stamp each entry
2378
//			pw.println(dateString);
2379
//			pw.flush();
2380
//		} catch (Exception e) {
2381
//			logReplication.error("error writing to replication log from "
2382
//					+ "MetacatReplication.replLog: " + e.getMessage());
2383
//			// e.printStackTrace(System.out);
2384
//		}
2385
//	}
2386

    
2387
//	/**
2388
//	 * Method for writing replication messages to a log file specified in
2389
//	 * metacat.properties
2390
//	 */
2391
//	public static void replErrorLog(String message) {
2392
//		try {
2393
//			FileOutputStream fos = new FileOutputStream(PropertyService
2394
//					.getProperty("replication.logdir")
2395
//					+ "/metacatreplicationerror.log", true);
2396
//			PrintWriter pw = new PrintWriter(fos);
2397
//			SimpleDateFormat formatter = new SimpleDateFormat("yy-MM-dd HH:mm:ss");
2398
//			java.util.Date localtime = new java.util.Date();
2399
//			String dateString = formatter.format(localtime);
2400
//			dateString += " :: " + message;
2401
//			//time stamp each entry
2402
//			pw.println(dateString);
2403
//			pw.flush();
2404
//		} catch (Exception e) {
2405
//			logReplication.error("error writing to replication error log from "
2406
//					+ "MetacatReplication.replErrorLog: " + e.getMessage());
2407
//			//e.printStackTrace(System.out);
2408
//		}
2409
//	}
2410

    
2411
	/**
2412
	 * Returns true if the replicate field for server in xml_replication is 1.
2413
	 * Returns false otherwise
2414
	 */
2415
	public static boolean replToServer(String server) {
2416
		DBConnection dbConn = null;
2417
		int serialNumber = -1;
2418
		PreparedStatement pstmt = null;
2419
		try {
2420
			dbConn = DBConnectionPool.getDBConnection("MetacatReplication.repltoServer");
2421
			serialNumber = dbConn.getCheckOutSerialNumber();
2422
			pstmt = dbConn.prepareStatement("select replicate from "
2423
					+ "xml_replication where server like ? ");
2424
			pstmt.setString(1, server);
2425
			pstmt.execute();
2426
			ResultSet rs = pstmt.getResultSet();
2427
			boolean tablehasrows = rs.next();
2428
			if (tablehasrows) {
2429
				int i = rs.getInt(1);
2430
				if (i == 1) {
2431
					pstmt.close();
2432
					//conn.close();
2433
					return true;
2434
				} else {
2435
					pstmt.close();
2436
					//conn.close();
2437
					return false;
2438
				}
2439
			}
2440
		} catch (SQLException sqle) {
2441
			logMetacat.error("ReplicationService.replToServer - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
2442
			logReplication.error("ReplicationService.replToServer - SQL error in MetacatReplication.replToServer: "
2443
					+ sqle.getMessage());
2444
		} finally {
2445
			try {
2446
				pstmt.close();
2447
				//conn.close();
2448
			}//try
2449
			catch (Exception ee) {
2450
				logMetacat.error("ReplicationService.replToServer - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
2451
				logReplication.error("ReplicationService.replToServer - Error in MetacatReplication.replToServer: "
2452
						+ ee.getMessage());
2453
			}//catch
2454
			finally {
2455
				DBConnectionPool.returnDBConnection(dbConn, serialNumber);
2456
			}//finally
2457
		}//finally
2458
		return false;
2459
		//the default if this server does not exist is to not replicate to it.
2460
	}
2461

    
2462
}
(6-6/7)