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: rnahf $'
9
 *     '$Date: 2015-03-17 16:57:15 -0700 (Tue, 17 Mar 2015) $'
10
 * '$Revision: 9151 $'
11
 *
12
 * This program is free software; you can redistribute it and/or modify
13
 * it under the terms of the GNU General Public License as published by
14
 * the Free Software Foundation; either version 2 of the License, or
15
 * (at your option) any later version.
16
 *
17
 * This program is distributed in the hope that it will be useful,
18
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20
 * GNU General Public License for more details.
21
 *
22
 * You should have received a copy of the GNU General Public License
23
 * along with this program; if not, write to the Free Software
24
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
25
 */
26

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

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

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

    
62
import org.apache.commons.io.IOUtils;
63
import org.apache.http.HttpResponse;
64
import org.apache.http.client.HttpClient;
65
import org.apache.http.impl.client.HttpClientBuilder;
66
import org.apache.http.impl.client.HttpClients;
67
import org.apache.http.client.config.RequestConfig;
68
import org.apache.log4j.Logger;
69
import org.dataone.client.auth.CertificateManager;
70
import org.dataone.client.rest.RestClient;
71
import org.dataone.client.utils.HttpUtils;
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 RestClient sslClient = null;
134
	private static int CLIENTTIMEOUT = 30000;
135

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
624
	}
625

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

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

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

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

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

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

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

    
871
		}//catch
872

    
873
	}
874

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1059
		try {
1060

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

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

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

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

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

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

    
1139
		String docInfoStr = getDocumentInfo(docid);
1140

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1381
	}
1382

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

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

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

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

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

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

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

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

    
1508
	}
1509

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1759
	}//handlUpdateRequest
1760

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

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

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

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

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

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

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

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

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

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

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

    
1936
		return null;
1937
	}
1938

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

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

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

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

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

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

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

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

    
2048
		try {
2049

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

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

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

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

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

    
2100
		try {
2101

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

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

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

    
2133
		return codes;
2134
	}
2135

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

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

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

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

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

    
2220
		try {
2221

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

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

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

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

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

    
2341
	        try {
2342
	            RequestConfig rc = RequestConfig.custom()
2343
	                    .setConnectionRequestTimeout(CLIENTTIMEOUT)
2344
	                    .setConnectTimeout(CLIENTTIMEOUT)
2345
	                    .setSocketTimeout(CLIENTTIMEOUT).build();
2346
	            HttpClient hc = HttpUtils.getHttpClientBuilder(HttpUtils.selectSession(subject))
2347
	                    .setDefaultRequestConfig(rc)
2348
	                    .build();
2349
	            
2350
	            sslClient = new RestClient(hc);
2351
	        } 
2352
	        catch (FileNotFoundException e) {
2353
	            // these are somewhat expected for anonymous client use
2354
	            logReplication.warn("Could not set up SSL connection for client - likely because the certificate could not be located: " + e.getMessage());
2355
	        }
2356
	        catch (Exception e) {
2357
	            // this is likely more severe
2358
	            logReplication.error("Failed to set up SSL connection for client. Continuing. " + e.getClass() + ":: " + e.getMessage(), e);
2359
	        }
2360
	    }
2361
		return sslClient;
2362
	}
2363

    
2364
	
2365

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

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

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

    
2465
}
(6-6/7)