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-24 13:14:09 -0700 (Tue, 24 Mar 2015) $'
10
 * '$Revision: 9156 $'
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.commons.lang.exception.ExceptionUtils;
64
import org.apache.http.HttpEntity;
65
import org.apache.http.HttpResponse;
66
import org.apache.http.StatusLine;
67
import org.apache.http.client.ClientProtocolException;
68
import org.apache.http.client.HttpClient;
69
import org.apache.http.client.HttpResponseException;
70
import org.apache.http.impl.client.HttpClientBuilder;
71
import org.apache.http.impl.client.HttpClients;
72
import org.apache.http.client.config.RequestConfig;
73
import org.apache.log4j.Logger;
74
import org.dataone.client.auth.CertificateManager;
75
import org.dataone.client.rest.RestClient;
76
import org.dataone.client.utils.HttpUtils;
77
import org.dataone.service.types.v1.Identifier;
78
import org.dataone.service.types.v2.SystemMetadata;
79
import org.dataone.service.util.DateTimeMarshaller;
80
import org.dataone.service.util.TypeMarshaller;
81
import org.jibx.runtime.JiBXException;
82
import org.xml.sax.InputSource;
83
import org.xml.sax.SAXException;
84
import org.xml.sax.XMLReader;
85

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

    
120
public class ReplicationService extends BaseService {
121

    
122
	private static ReplicationService replicationService = null;
123

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

    
141
	public static final String REPLICATION_LOG_FILE_NAME = "metacatreplication.log";
142

    
143
	private static String DATA_FILE_FLAG = null;
144
	public static String METACAT_REPL_ERROR_MSG = null;
145
	private static Logger logReplication = Logger.getLogger("ReplicationLogging");
146
	private static Logger logMetacat = Logger.getLogger(ReplicationService.class);
147

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

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

    
190
			String timeIntervalStr = 
191
				PropertyService.getProperty("replication.timedreplicationinterval");
192
			timeInterval = (new Long(timeIntervalStr)).longValue();
193
			logReplication.info("ReplicationService.initialize - The timed replication time Interval is " + timeInterval);
194

    
195
			String firstTimeStr = 
196
				PropertyService.getProperty("replication.firsttimedreplication");
197
			logReplication.info("ReplicationService.initialize - first replication time form property is " + firstTimeStr);
198
			firstTime = ReplicationHandler.combinateCurrentDateAndGivenTime(firstTimeStr);
199

    
200
			logReplication.info("ReplicationService.initialize - After combine current time, the real first time is "
201
					+ firstTime.toString() + " minisec");
202

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

    
211
		} catch (PropertyNotFoundException pnfe) {
212
			throw new ServiceException(
213
					"ReplicationService.initialize - Property error while instantiating "
214
							+ "replication service: " + pnfe.getMessage());
215
		} catch (HandlerException he) {
216
			throw new ServiceException(
217
					"ReplicationService.initialize - Handler error while instantiating "
218
							+ "replication service" + he.getMessage());
219
		} 
220
	}
221

    
222
	/**
223
	 * Get the single instance of SessionService.
224
	 * 
225
	 * @return the single instance of SessionService
226
	 */
227
	public static ReplicationService getInstance() throws ServiceException {
228
		if (replicationService == null) {
229
			replicationService = new ReplicationService();
230
		}
231
		return replicationService;
232
	}
233

    
234
	public boolean refreshable() {
235
		return true;
236
	}
237

    
238
	protected void doRefresh() throws ServiceException {
239
		return;
240
	}
241
	
242
	public void stop() throws ServiceException{
243
		
244
	}
245

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

    
257
	      logReplication.info("ReplicationService.stopReplication - deltaT handler stopped");
258
		return;
259
	}
260
	
261
	public void startReplication(Hashtable<String, String[]> params) throws ServiceException {
262

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

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

    
357
			// add server to server list
358
			if (subaction.equals("add")) {
359
				replicate = ((String[]) params.get("replicate"))[0];
360
				server = ((String[]) params.get("server"))[0];
361

    
362
				//Get data replication value
363
				dataReplicate = ((String[]) params.get("datareplicate"))[0];
364
				
365
				//Get hub value
366
				hub = ((String[]) params.get("hub"))[0];
367

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

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

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

    
595
				tablehasrows = rs.next();
596
			}
597
			out.write("</table></body></html>");
598
			
599
			
600
			pstmt.close();
601
			//conn.close();
602

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

    
629
	}
630

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

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

    
665
			// get the document info from server
666
			URL docinfourl = new URL("https://" + server + "?server="
667
					+ MetacatUtil.getLocalReplicationServerName()
668
					+ "&action=getdocumentinfo&docid=" + docid);
669
			
670

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

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

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

    
836
	/*
837
	 * when a forcereplication delete request comes in, local host will delete this
838
	 * document
839
	 */
840
	protected static void handleForceReplicateDeleteRequest(
841
			Hashtable<String, String[]> params, HttpServletResponse response,
842
			HttpServletRequest request, boolean removeAll) {
843
		String server = ((String[]) params.get("server"))[0]; // the server that
844
		String docid = ((String[]) params.get("docid"))[0]; // sent the document
845
		try {
846
			logReplication.info("ReplicationService.handleForceReplicateDeleteRequest - force replication delete request from " + server);
847
			logReplication.info("ReplicationService.handleForceReplicateDeleteRequest - force replication delete docid " + docid);
848
			logReplication.info("ReplicationService.handleForceReplicateDeleteRequest - Force replication delete request from: " + server);
849
			logReplication.info("ReplicationService.handleForceReplicateDeleteRequest - Force replication delete docid: " + docid);
850
			DocumentImpl.delete(docid, null, null, server, removeAll);
851
			logReplication.info("ReplicationService.handleForceReplicateDeleteRequest - document " + docid + " was successfully deleted ");
852
			EventLog.getInstance().log(request.getRemoteAddr(), request.getHeader("User-Agent"), REPLICATIONUSER, docid,
853
					"delete");
854
			logReplication.info("ReplicationService.handleForceReplicateDeleteRequest - document " + docid + " was successfully deleted ");
855
		} catch (McdbDocNotFoundException 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 (InsufficientKarmaException 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 (SQLException 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
		} catch (Exception e) {
871
			logMetacat.error("ReplicationService.handleForceReplicateDeleteRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
872
			logReplication.error("document " + docid
873
					+ " failed to delete because " + e.getMessage());
874
			logReplication.error("ReplicationService.handleForceReplicateDeleteRequest - error: " + e.getMessage());
875

    
876
		}//catch
877

    
878
	}
879

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

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

    
903
		// Overide or not
904
		//    boolean override = false;
905
		// dbaction - update or insert
906
		String dbaction = null;
907

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

    
920
			logReplication.info("ReplicationService.handleForceReplicateDataFileRequest - Force replication request from: " + server);
921
			logReplication.info("ReplicationService.handleForceReplicateDataFileRequest - Force replication docid: " + docid);
922
			logReplication.info("ReplicationService.handleForceReplicateDataFileRequest - Force replication action: " + dbaction);
923
			// get the document info from server
924
			URL docinfourl = new URL("https://" + server + "?server="
925
					+ MetacatUtil.getLocalReplicationServerName()
926
					+ "&action=getdocumentinfo&docid=" + docid);
927

    
928
			String docInfoStr = ReplicationService.getURLContent(docinfourl);
929
			
930
			// strip out the system metadata portion
931
		    String systemMetadataXML = ReplicationUtil.getSystemMetadataContent(docInfoStr);
932
		   	docInfoStr = ReplicationUtil.getContentWithoutSystemMetadata(docInfoStr);
933

    
934
			//dih is the parser for the docinfo xml format
935
			DocInfoHandler dih = new DocInfoHandler();
936
			XMLReader docinfoParser = ReplicationHandler.initParser(dih);
937
			docinfoParser.parse(new InputSource(new StringReader(docInfoStr)));
938
			Hashtable<String, String> docinfoHash = dih.getDocInfo();
939
			
940
			String docName = (String) docinfoHash.get("docname");
941

    
942
			String docType = (String) docinfoHash.get("doctype");
943

    
944
			String docHomeServer = (String) docinfoHash.get("home_server");
945
			
946
			String createdDateString = docinfoHash.get("date_created");
947
			String updatedDateString = docinfoHash.get("date_updated");
948
			
949
			Date createdDate = DateTimeMarshaller.deserializeDateToUTC(createdDateString);
950
			Date updatedDate = DateTimeMarshaller.deserializeDateToUTC(updatedDateString);
951
			
952
			logReplication.info("ReplicationService.handleForceReplicateDataFileRequest - docHomeServer of datafile: " + docHomeServer);
953

    
954
			// in case we have a write exception, we still want to track access and sysmeta
955
			Exception writeException = null;
956

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

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

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

    
1038
			logReplication.info("ReplicationService.handleForceReplicateDataFileRequest - datafile " + docid + " added to DB with "
1039
					+ "action " + dbaction);
1040
			EventLog.getInstance().log(request.getRemoteAddr(), request.getHeader("User-Agent"), REPLICATIONUSER,
1041
					docid, dbaction);
1042

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

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

    
1064
		try {
1065

    
1066
			String docid = ((String[]) params.get("docid"))[0];
1067
			String remoteRev = ((String[]) params.get("updaterev"))[0];
1068
			DocumentImpl requestDoc = new DocumentImpl(docid);
1069
			logReplication.info("ReplicationService.handleGetLockRequest - lock request for " + docid);
1070
			int localRevInt = requestDoc.getRev();
1071
			int remoteRevInt = Integer.parseInt(remoteRev);
1072

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

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

    
1118
		try {
1119
			// get docinfo as XML string
1120
			String docinfoXML = getDocumentInfo(docid);
1121
			
1122
			// get a writer for sending back to response
1123
			response.setContentType("text/xml");
1124
			Writer out = response.getWriter();
1125
			out.write(docinfoXML);
1126
			out.close();
1127

    
1128
		} catch (Exception e) {
1129
			logMetacat.error("ReplicationService.handleGetDocumentInfoRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
1130
			logReplication.error("ReplicationService.handleGetDocumentInfoRequest - error in metacatReplication.handlegetdocumentinforequest "
1131
					+ "for doc: " + docid + " : " + e.getMessage());
1132
		}
1133

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

    
1144
		String docInfoStr = getDocumentInfo(docid);
1145

    
1146
		// strip out the system metadata portion
1147
		String systemMetadataXML = ReplicationUtil.getSystemMetadataContent(docInfoStr);
1148
		docInfoStr = ReplicationUtil.getContentWithoutSystemMetadata(docInfoStr);
1149

    
1150
		docinfoParser.parse(new InputSource(new StringReader(docInfoStr)));
1151
		Hashtable<String, String> docinfoHash = dih.getDocInfo();
1152

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

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

    
1210
		sb.append("<accessControl>");
1211

    
1212
		AccessControlForSingleFile acfsf = new AccessControlForSingleFile(docid); 
1213
		sb.append(acfsf.getAccessString());
1214
		
1215
		sb.append("</accessControl>");
1216

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

    
1243
		} catch (Exception e) {
1244
			String msg = "ReplicationService.handleGetSystemMetadataRequest for guid: " + guid + " : " + e.getMessage();
1245
			logMetacat.error(msg);                         
1246
			logReplication.error(msg);
1247
		}
1248

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

    
1285
		} catch (Exception e) {
1286
			logMetacat.error("ReplicationService.handleForceReplicateRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG, e);                         
1287
			logReplication.error("ReplicationService.handleForceReplicateRequest - General error when processing guid: " + guid, e);
1288
		}
1289
	}
1290

    
1291
	/**
1292
	 * Sends a datafile to a remote host
1293
	 */
1294
	protected static void handleGetDataFileRequest(OutputStream outPut,
1295
			Hashtable<String, String[]> params, HttpServletResponse response)
1296

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

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

    
1330
		if (!filepath.endsWith("/")) {
1331
			filepath += "/";
1332
		}
1333
		// Get file aboslute file name
1334
		String filename = filepath + docId;
1335

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

    
1357
		// Set the mime type
1358
		response.setContentType(contentType);
1359

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

    
1377
		} catch (Exception e) {
1378
			logMetacat.error("ReplicationService.handleGetDataFileRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
1379
			logReplication.error("ReplicationService.handleGetDataFileRequest - error getting data file from MetacatReplication."
1380
					+ "handlGetDataFileRequest " + e.getMessage());
1381
			e.printStackTrace(System.out);
1382
		} finally {
1383
		    IOUtils.closeQuietly(fin);
1384
		}
1385

    
1386
	}
1387

    
1388
	/**
1389
	 * Sends a document to a remote host
1390
	 */
1391
	protected static void handleGetDocumentRequest(
1392
			Hashtable<String, String[]> params, HttpServletResponse response) {
1393

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

    
1418
			String docid = params.get("docid")[0];
1419
			logReplication.debug("ReplicationService.handleGetDocumentRequest - MetacatReplication.handleGetDocumentRequest for docid: "
1420
					+ docid);
1421
			DocumentImpl di = new DocumentImpl(docid);
1422

    
1423
			String documentDir = PropertyService
1424
					.getProperty("application.documentfilepath");
1425
			documentPath = documentDir + FileUtil.getFS() + docid;
1426

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

    
1437
			// read the file from disk and send it to outputstream
1438
			outputStream = response.getOutputStream();
1439
			is = di.readFromFileSystem(outputStream, null, null, documentPath);
1440
			is.close();
1441
			outputStream.close();
1442

    
1443
			logReplication.info("ReplicationService.handleGetDocumentRequest - document " + docid + " sent");
1444

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

    
1513
	}
1514

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

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

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

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

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

    
1585
			// Store the sql command
1586
			StringBuffer docsql = new StringBuffer();
1587
			StringBuffer revisionSql = new StringBuffer();
1588
			
1589
			// Store the data set file
1590
			Vector<Vector<String>> packageFiles = new Vector<Vector<String>>();
1591

    
1592
			// Append local server's name and replication servlet to doclist
1593
			out.append("<?xml version=\"1.0\"?><replication>");
1594
			out.append("<server>")
1595
					.append(MetacatUtil.getLocalReplicationServerName());
1596
			//doclist.append(util.getProperty("replicationpath"));
1597
			out.append("</server><updates>");
1598

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

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

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

    
1702
			pstmt = dbConn.prepareStatement(delsql.toString());
1703
			//usage count should increas 1
1704
			dbConn.increaseUsageCount(1);
1705

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

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

    
1730
			out.append("</updates></replication>");
1731
			logReplication.info("ReplicationService.handleUpdateRequest - done writing to output stream.");
1732
			pstmt.close();
1733
			//conn.close();
1734

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

    
1764
	}//handlUpdateRequest
1765

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

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

    
1807
		pstmt.execute();
1808
		ResultSet rs = pstmt.getResultSet();
1809
		logReplication.warn("Processing replication revision for documents");
1810
		while (rs.next()) {
1811
			String recordDoctype = rs.getString(3);
1812

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

    
1817
				// do nothing
1818
			} else {
1819
				String docid = rs.getString(1);
1820
				int rev = rs.getInt(2);
1821
				logMetacat.debug("Processing replication revision for docid: " + docid + "." + rev);
1822

    
1823
				revDocList.append("<revisionDocument>");
1824
				revDocList.append("<docid>").append(docid);
1825
				revDocList.append("</docid><rev>").append(rev);
1826
				revDocList.append("</rev>");
1827
				// data file
1828
				if (recordDoctype.equals("BIN")) {
1829
					revDocList.append("<datafile>");
1830
					revDocList.append(DATA_FILE_FLAG);
1831
					revDocList.append("</datafile>");
1832
				}
1833
				revDocList.append("</revisionDocument>");
1834

    
1835
			}//else
1836
		}
1837
		//System.out.println("The revision list is"+ revDocList.toString());
1838
		return revDocList.toString();
1839
	}
1840

    
1841
	/**
1842
	 * Returns the xml_catalog table encoded in xml
1843
	 */
1844
	public static String getCatalogXML() {
1845
		return handleGetCatalogRequest(null, null, false);
1846
	}
1847

    
1848
	/**
1849
	 * Sends the contents of the xml_catalog table encoded in xml
1850
	 * The xml format is:
1851
	 * <!ELEMENT xml_catalog (row*)>
1852
	 * <!ELEMENT row (entry_type, source_doctype, target_doctype, public_id,
1853
	 *                system_id)>
1854
	 * All of the sub elements of row are #PCDATA
1855

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

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

    
1941
		return null;
1942
	}
1943

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

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

    
1978
			Thread.sleep(30000); //the lock will expire in 30 seconds
1979
			logReplication.info("thread for docid: "
1980
					+ (String) fileLocks.elementAt(fileLocks.size() - 1) + " exiting.");
1981

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

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

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

    
2037
		return null;
2038
		//return null if the server does not exist
2039
	}
2040

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

    
2053
		try {
2054

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

    
2071
		} catch (SQLException sqle) {
2072
			throw new ServiceException("ReplicationService.getServerCodeForServerName - " 
2073
					+ "SQL error when getting server code: " + sqle.getMessage());
2074

    
2075
		} finally {
2076
			try {
2077
				pstmt.close();
2078
				//conn.close();
2079
			}//try
2080
			catch (Exception ee) {
2081
				logMetacat.error("ReplicationService.getServerCodeForServerName - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
2082
				logReplication.error("ReplicationService.getServerNameForServerCode - Error in MetacatReplicatio.getServerCode: "
2083
						+ ee.getMessage());
2084

    
2085
			}//catch
2086
			finally {
2087
				DBConnectionPool.returnDBConnection(dbConn, serialNumber);
2088
			}//finally
2089
		}//finally
2090

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

    
2105
		try {
2106

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

    
2123
		} finally {
2124
			try {
2125
				pstmt.close();
2126
			}//try
2127
			catch (Exception ee) {
2128
				logMetacat.error("ReplicationService.getServerCodes - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
2129
				logReplication.error("ReplicationService.getServerCodes - Error in MetacatReplicatio.getServerCodes: "
2130
						+ ee.getMessage());
2131

    
2132
			}//catch
2133
			finally {
2134
				DBConnectionPool.returnDBConnection(dbConn, serialNumber);
2135
			}//finally
2136
		}//finally
2137

    
2138
		return codes;
2139
	}
2140

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

    
2181
				String server = rs.getString(1);
2182
				String last_checked = rs.getString(2);
2183
				if (!server.equals("localhost")) {
2184
					sl.put(server, last_checked);
2185
				}
2186

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

    
2209
		}//finally
2210
		return sl;
2211
	}
2212

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

    
2225
		try {
2226

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

    
2252
		} catch (SQLException sqle) {
2253
			throw new ServiceException("ReplicationService.getHomeServerCodeForDocId - " 
2254
					+ "SQL error when getting home server code for docid: " + docId + " : " 
2255
					+ sqle.getMessage());
2256

    
2257
		} finally {
2258
			try {
2259
				pstmt.close();
2260
				//conn.close();
2261

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

    
2274
	/**
2275
	 * This method returns the content of a url
2276
	 * @param u the url to return the content from
2277
	 * @return a string representing the content of the url
2278
	 * @throws java.io.IOException
2279
	 */
2280
	public static String getURLContent(URL u) throws Exception {
2281
		char istreamChar;
2282
		int istreamInt;
2283
		// get the response content
2284
		InputStream input = null;
2285
		String content = null;
2286
		try {
2287
		    input = getURLStream(u);
2288
		    logReplication.info("ReplicationService.getURLContent - After getting response from: " + u.toString());
2289
		    content = IOUtils.toString(input, "UTF-8");
2290
		} 
2291
		finally {
2292
		    IOUtils.closeQuietly(input);
2293
		}
2294
        return content;
2295
	}
2296
	
2297
	/**
2298
	 * This method returns the InputStream after opening a url
2299
	 * @param u the url to return the content from
2300
	 * @return a InputStream representing the content of the url
2301
	 * @throws java.io.IOException
2302
	 */
2303
	public static InputStream getURLStream(URL u) throws Exception {
2304
	    logReplication.info("Getting url stream from " + u.toString());
2305
	    logReplication.info("ReplicationService.getURLStream - Before sending request to: " + u.toString());
2306
	    // use httpclient to set up SSL
2307

    
2308
	    InputStream input = null;
2309
	    try {
2310
	        RestClient client = getSSLClient();
2311
	        HttpResponse response = client.doGetRequest(u.toString(),null);
2312
	        // get the response content
2313
	        StatusLine statusLine = response.getStatusLine();
2314
	        HttpEntity entity = response.getEntity();
2315
	        logReplication.info("ReplicationService.getURLStream - After getting response from: " + u.toString());
2316
	        if (statusLine.getStatusCode() >= 300) {
2317
	            throw new HttpResponseException(
2318
	                    statusLine.getStatusCode(),
2319
	                    "ReplicationService.getURLStream - " + statusLine.getReasonPhrase());
2320
	        }
2321
	        if (entity == null) {
2322
	            throw new ClientProtocolException("ReplicationService.getURLStream - Response contains no content");
2323
	        }
2324
	        input = entity.getContent();
2325
	    } 
2326
	    catch (Throwable t) {
2327
	        logReplication.error("Unexpected Throwable encountered.  Logging and moving on: " +
2328
	                t.getClass().getCanonicalName() + ": " + t.getMessage());
2329
	        logReplication.error(ExceptionUtils.getStackTrace(t));
2330
	        throw new Exception(t);
2331
	    }
2332
		return input;
2333
	}
2334
	
2335
	/**
2336
     * This method returns a byte array after opening a url
2337
     * @param u the url to return the content from
2338
     * @return a InputStream representing the content of the url
2339
     * @throws java.io.IOException
2340
     */
2341
    public static byte[] getURLBytes(URL u) throws Exception {
2342
        InputStream input = null;
2343
        try {
2344
            input = getURLStream(u);
2345
            byte[] bytes = IOUtils.toByteArray(input);
2346
            return bytes;
2347
        } 
2348
        finally {
2349
            IOUtils.closeQuietly(input);
2350
        }
2351
    }
2352
	
2353
	/**
2354
	 * Sets up an HttpClient with SSL connection.
2355
	 * Sends client certificate to the server when doing the request.
2356
	 * @return
2357
	 */
2358
	private static RestClient getSSLClient() {
2359
		
2360
	    if (sslClient == null) {
2361
		
2362
	        // set up this server's client identity
2363
	        String subject = null;
2364
	        try {
2365
	            // TODO: should there be alternative ways to get the key and certificate?
2366
	            String certificateFile = PropertyService.getProperty("replication.certificate.file");
2367
	            String keyFile = PropertyService.getProperty("replication.privatekey.file");
2368
	            String keyPassword = PropertyService.getProperty("replication.privatekey.password");
2369
	            X509Certificate certificate = CertificateManager.getInstance().loadCertificateFromFile(certificateFile);
2370
	            PrivateKey privateKey = CertificateManager.getInstance().loadPrivateKeyFromFile(keyFile, keyPassword);
2371
	            subject = CertificateManager.getInstance().getSubjectDN(certificate);
2372
	            CertificateManager.getInstance().registerCertificate(subject, certificate, privateKey);
2373
	        } catch (Exception e) {
2374
	            // this is pretty much required for replication communication
2375
	            logReplication.warn("Could not find server's client certificate/private key: " + e.getMessage());
2376
	        }
2377

    
2378
	        try {
2379
	            RequestConfig rc = RequestConfig.custom()
2380
	                    .setConnectionRequestTimeout(CLIENTTIMEOUT)
2381
	                    .setConnectTimeout(CLIENTTIMEOUT)
2382
	                    .setSocketTimeout(CLIENTTIMEOUT).build();
2383
	            HttpClient hc = HttpUtils.getHttpClientBuilder(HttpUtils.selectSession(subject))
2384
	                    .setDefaultRequestConfig(rc)
2385
	                    .build();
2386
	            
2387
	            sslClient = new RestClient(hc);
2388
	        } 
2389
	        catch (FileNotFoundException e) {
2390
	            // these are somewhat expected for anonymous client use
2391
	            logReplication.warn("Could not set up SSL connection for client - likely because the certificate could not be located: " + e.getMessage());
2392
	        }
2393
	        catch (Exception e) {
2394
	            // this is likely more severe
2395
	            logReplication.error("Failed to set up SSL connection for client. Continuing. " + e.getClass() + ":: " + e.getMessage(), e);
2396
	        }
2397
	    }
2398
		return sslClient;
2399
	}
2400

    
2401
	
2402

    
2403
//	/**
2404
//	 * Method for writing replication messages to a log file specified in
2405
//	 * metacat.properties
2406
//	 */
2407
//	public static void replLog(String message) {
2408
//		try {
2409
//			FileOutputStream fos = new FileOutputStream(PropertyService
2410
//					.getProperty("replication.logdir")
2411
//					+ "/metacatreplication.log", true);
2412
//			PrintWriter pw = new PrintWriter(fos);
2413
//			SimpleDateFormat formatter = new SimpleDateFormat("yy-MM-dd HH:mm:ss");
2414
//			java.util.Date localtime = new java.util.Date();
2415
//			String dateString = formatter.format(localtime);
2416
//			dateString += " :: " + message;
2417
//			// time stamp each entry
2418
//			pw.println(dateString);
2419
//			pw.flush();
2420
//		} catch (Exception e) {
2421
//			logReplication.error("error writing to replication log from "
2422
//					+ "MetacatReplication.replLog: " + e.getMessage());
2423
//			// e.printStackTrace(System.out);
2424
//		}
2425
//	}
2426

    
2427
//	/**
2428
//	 * Method for writing replication messages to a log file specified in
2429
//	 * metacat.properties
2430
//	 */
2431
//	public static void replErrorLog(String message) {
2432
//		try {
2433
//			FileOutputStream fos = new FileOutputStream(PropertyService
2434
//					.getProperty("replication.logdir")
2435
//					+ "/metacatreplicationerror.log", true);
2436
//			PrintWriter pw = new PrintWriter(fos);
2437
//			SimpleDateFormat formatter = new SimpleDateFormat("yy-MM-dd HH:mm:ss");
2438
//			java.util.Date localtime = new java.util.Date();
2439
//			String dateString = formatter.format(localtime);
2440
//			dateString += " :: " + message;
2441
//			//time stamp each entry
2442
//			pw.println(dateString);
2443
//			pw.flush();
2444
//		} catch (Exception e) {
2445
//			logReplication.error("error writing to replication error log from "
2446
//					+ "MetacatReplication.replErrorLog: " + e.getMessage());
2447
//			//e.printStackTrace(System.out);
2448
//		}
2449
//	}
2450

    
2451
	/**
2452
	 * Returns true if the replicate field for server in xml_replication is 1.
2453
	 * Returns false otherwise
2454
	 */
2455
	public static boolean replToServer(String server) {
2456
		DBConnection dbConn = null;
2457
		int serialNumber = -1;
2458
		PreparedStatement pstmt = null;
2459
		try {
2460
			dbConn = DBConnectionPool.getDBConnection("MetacatReplication.repltoServer");
2461
			serialNumber = dbConn.getCheckOutSerialNumber();
2462
			pstmt = dbConn.prepareStatement("select replicate from "
2463
					+ "xml_replication where server like ? ");
2464
			pstmt.setString(1, server);
2465
			pstmt.execute();
2466
			ResultSet rs = pstmt.getResultSet();
2467
			boolean tablehasrows = rs.next();
2468
			if (tablehasrows) {
2469
				int i = rs.getInt(1);
2470
				if (i == 1) {
2471
					pstmt.close();
2472
					//conn.close();
2473
					return true;
2474
				} else {
2475
					pstmt.close();
2476
					//conn.close();
2477
					return false;
2478
				}
2479
			}
2480
		} catch (SQLException sqle) {
2481
			logMetacat.error("ReplicationService.replToServer - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
2482
			logReplication.error("ReplicationService.replToServer - SQL error in MetacatReplication.replToServer: "
2483
					+ sqle.getMessage());
2484
		} finally {
2485
			try {
2486
				pstmt.close();
2487
				//conn.close();
2488
			}//try
2489
			catch (Exception ee) {
2490
				logMetacat.error("ReplicationService.replToServer - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
2491
				logReplication.error("ReplicationService.replToServer - Error in MetacatReplication.replToServer: "
2492
						+ ee.getMessage());
2493
			}//catch
2494
			finally {
2495
				DBConnectionPool.returnDBConnection(dbConn, serialNumber);
2496
			}//finally
2497
		}//finally
2498
		return false;
2499
		//the default if this server does not exist is to not replicate to it.
2500
	}
2501

    
2502
}
(6-6/7)