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: tao $'
9
 *     '$Date: 2015-05-11 22:43:21 -0700 (Mon, 11 May 2015) $'
10
 * '$Revision: 9206 $'
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
				
968
				
969
				//register data file into xml_documents table and write data file
970
				//into file system
971
				InputStream inputStream = null;
972
				try {
973
				    inputStream = getURLStream(url);
974
					DocumentImpl.writeDataFileInReplication(inputStream,
975
							datafilePath, docName, docType, docid, null, docHomeServer,
976
							server, DocumentImpl.DOCUMENTTABLE, false, createdDate,
977
							updatedDate);
978
				} catch (Exception e) {
979
					writeException = e;
980
				} finally {
981
				    IOUtils.closeQuietly(inputStream);
982
				}
983

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

    
1042
			logReplication.info("ReplicationService.handleForceReplicateDataFileRequest - datafile " + docid + " added to DB with "
1043
					+ "action " + dbaction);
1044
			EventLog.getInstance().log(request.getRemoteAddr(), request.getHeader("User-Agent"), REPLICATIONUSER,
1045
					docid, dbaction);
1046

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

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

    
1068
		try {
1069

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

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

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

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

    
1132
		} catch (Exception e) {
1133
			logMetacat.error("ReplicationService.handleGetDocumentInfoRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
1134
			logReplication.error("ReplicationService.handleGetDocumentInfoRequest - error in metacatReplication.handlegetdocumentinforequest "
1135
					+ "for doc: " + docid + " : " + e.getMessage());
1136
		}
1137

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

    
1148
		String docInfoStr = getDocumentInfo(docid);
1149

    
1150
		// strip out the system metadata portion
1151
		String systemMetadataXML = ReplicationUtil.getSystemMetadataContent(docInfoStr);
1152
		docInfoStr = ReplicationUtil.getContentWithoutSystemMetadata(docInfoStr);
1153

    
1154
		docinfoParser.parse(new InputSource(new StringReader(docInfoStr)));
1155
		Hashtable<String, String> docinfoHash = dih.getDocInfo();
1156

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

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

    
1214
		sb.append("<accessControl>");
1215

    
1216
		AccessControlForSingleFile acfsf = new AccessControlForSingleFile(docid); 
1217
		sb.append(acfsf.getAccessString());
1218
		
1219
		sb.append("</accessControl>");
1220

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

    
1247
		} catch (Exception e) {
1248
			String msg = "ReplicationService.handleGetSystemMetadataRequest for guid: " + guid + " : " + e.getMessage();
1249
			logMetacat.error(msg);                         
1250
			logReplication.error(msg);
1251
		}
1252

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

    
1289
		} catch (Exception e) {
1290
			logMetacat.error("ReplicationService.handleForceReplicateRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG, e);                         
1291
			logReplication.error("ReplicationService.handleForceReplicateRequest - General error when processing guid: " + guid, e);
1292
		}
1293
	}
1294

    
1295
	/**
1296
	 * Sends a datafile to a remote host
1297
	 */
1298
	protected static void handleGetDataFileRequest(OutputStream outPut,
1299
			Hashtable<String, String[]> params, HttpServletResponse response)
1300

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

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

    
1334
		if (!filepath.endsWith("/")) {
1335
			filepath += "/";
1336
		}
1337
		// Get file aboslute file name
1338
		String filename = filepath + docId;
1339

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

    
1361
		// Set the mime type
1362
		response.setContentType(contentType);
1363

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

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

    
1390
	}
1391

    
1392
	/**
1393
	 * Sends a document to a remote host
1394
	 */
1395
	protected static void handleGetDocumentRequest(
1396
			Hashtable<String, String[]> params, HttpServletResponse response) {
1397

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

    
1422
			String docid = params.get("docid")[0];
1423
			logReplication.debug("ReplicationService.handleGetDocumentRequest - MetacatReplication.handleGetDocumentRequest for docid: "
1424
					+ docid);
1425
			DocumentImpl di = new DocumentImpl(docid);
1426

    
1427
			String documentDir = PropertyService
1428
					.getProperty("application.documentfilepath");
1429
			documentPath = documentDir + FileUtil.getFS() + docid;
1430

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

    
1441
			// read the file from disk and send it to outputstream
1442
			outputStream = response.getOutputStream();
1443
			is = di.readFromFileSystem(outputStream, null, null, documentPath);
1444
			is.close();
1445
			outputStream.close();
1446

    
1447
			logReplication.info("ReplicationService.handleGetDocumentRequest - document " + docid + " sent");
1448

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

    
1517
	}
1518

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

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

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

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

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

    
1589
			// Store the sql command
1590
			StringBuffer docsql = new StringBuffer();
1591
			StringBuffer revisionSql = new StringBuffer();
1592
			
1593
			// Store the data set file
1594
			Vector<Vector<String>> packageFiles = new Vector<Vector<String>>();
1595

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

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

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

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

    
1706
			pstmt = dbConn.prepareStatement(delsql.toString());
1707
			//usage count should increas 1
1708
			dbConn.increaseUsageCount(1);
1709

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

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

    
1734
			out.append("</updates></replication>");
1735
			logReplication.info("ReplicationService.handleUpdateRequest - done writing to output stream.");
1736
			pstmt.close();
1737
			//conn.close();
1738

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

    
1768
	}//handlUpdateRequest
1769

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

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

    
1811
		pstmt.execute();
1812
		ResultSet rs = pstmt.getResultSet();
1813
		logReplication.warn("Processing replication revision for documents");
1814
		while (rs.next()) {
1815
			String recordDoctype = rs.getString(3);
1816

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

    
1821
				// do nothing
1822
			} else {
1823
				String docid = rs.getString(1);
1824
				int rev = rs.getInt(2);
1825
				logMetacat.debug("Processing replication revision for docid: " + docid + "." + rev);
1826

    
1827
				revDocList.append("<revisionDocument>");
1828
				revDocList.append("<docid>").append(docid);
1829
				revDocList.append("</docid><rev>").append(rev);
1830
				revDocList.append("</rev>");
1831
				// data file
1832
				if (recordDoctype.equals("BIN")) {
1833
					revDocList.append("<datafile>");
1834
					revDocList.append(DATA_FILE_FLAG);
1835
					revDocList.append("</datafile>");
1836
				}
1837
				revDocList.append("</revisionDocument>");
1838

    
1839
			}//else
1840
		}
1841
		//System.out.println("The revision list is"+ revDocList.toString());
1842
		return revDocList.toString();
1843
	}
1844

    
1845
	/**
1846
	 * Returns the xml_catalog table encoded in xml
1847
	 */
1848
	public static String getCatalogXML() {
1849
		return handleGetCatalogRequest(null, null, false);
1850
	}
1851

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

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

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

    
1945
		return null;
1946
	}
1947

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

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

    
1982
			Thread.sleep(30000); //the lock will expire in 30 seconds
1983
			logReplication.info("thread for docid: "
1984
					+ (String) fileLocks.elementAt(fileLocks.size() - 1) + " exiting.");
1985

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

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

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

    
2041
		return null;
2042
		//return null if the server does not exist
2043
	}
2044

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

    
2057
		try {
2058

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

    
2075
		} catch (SQLException sqle) {
2076
			throw new ServiceException("ReplicationService.getServerCodeForServerName - " 
2077
					+ "SQL error when getting server code: " + sqle.getMessage());
2078

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

    
2089
			}//catch
2090
			finally {
2091
				DBConnectionPool.returnDBConnection(dbConn, serialNumber);
2092
			}//finally
2093
		}//finally
2094

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

    
2109
		try {
2110

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

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

    
2136
			}//catch
2137
			finally {
2138
				DBConnectionPool.returnDBConnection(dbConn, serialNumber);
2139
			}//finally
2140
		}//finally
2141

    
2142
		return codes;
2143
	}
2144

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

    
2185
				String server = rs.getString(1);
2186
				String last_checked = rs.getString(2);
2187
				if (!server.equals("localhost")) {
2188
					sl.put(server, last_checked);
2189
				}
2190

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

    
2213
		}//finally
2214
		return sl;
2215
	}
2216

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

    
2229
		try {
2230

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

    
2256
		} catch (SQLException sqle) {
2257
			throw new ServiceException("ReplicationService.getHomeServerCodeForDocId - " 
2258
					+ "SQL error when getting home server code for docid: " + docId + " : " 
2259
					+ sqle.getMessage());
2260

    
2261
		} finally {
2262
			try {
2263
				pstmt.close();
2264
				//conn.close();
2265

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

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

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

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

    
2405
	
2406

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

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

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

    
2506
}
(6-6/7)