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: 2016-09-09 21:37:43 -0700 (Fri, 09 Sep 2016) $'
10
 * '$Revision: 9958 $'
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.types.AutoCloseHttpClientInputStream;
77
import org.dataone.client.utils.HttpUtils;
78
import org.dataone.exceptions.MarshallingException;
79
import org.dataone.service.types.v1.Identifier;
80
import org.dataone.service.types.v2.SystemMetadata;
81
import org.dataone.service.util.DateTimeMarshaller;
82
import org.dataone.service.util.TypeMarshaller;
83
import org.xml.sax.InputSource;
84
import org.xml.sax.SAXException;
85
import org.xml.sax.XMLReader;
86

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

    
121
public class ReplicationService extends BaseService {
122

    
123
	private static ReplicationService replicationService = null;
124

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
630
	}
631

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

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

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

    
672
			String docInfoStr = ReplicationService.getURLContent(docinfourl);
673
			// strip out the system metadata portion
674
			String systemMetadataXML = ReplicationUtil.getSystemMetadataContent(docInfoStr);
675
			docInfoStr = ReplicationUtil.getContentWithoutSystemMetadata(docInfoStr);
676
		   	  
677
			//dih is the parser for the docinfo xml format
678
			DocInfoHandler dih = new DocInfoHandler();
679
			XMLReader docinfoParser = ReplicationHandler.initParser(dih);
680
			docinfoParser.parse(new InputSource(new StringReader(docInfoStr)));
681
			//      Hashtable<String,Vector<AccessControlForSingleFile>> docinfoHash = dih.getDocInfo();
682
			Hashtable<String, String> docinfoHash = dih.getDocInfo();
683
			
684
			// Get home server of this docid
685
			String homeServer = (String) docinfoHash.get("home_server");
686
			
687
			// process system metadata
688
			SystemMetadata sysMeta = null;
689
			if (systemMetadataXML != null) {
690
				sysMeta = 
691
					TypeMarshaller.unmarshalTypeFromStream(
692
							SystemMetadata.class,
693
							new ByteArrayInputStream(systemMetadataXML.getBytes("UTF-8")));
694
				// need the guid-to-docid mapping
695
				boolean mappingExists = true;
696
		      	mappingExists = IdentifierManager.getInstance().mappingExists(sysMeta.getIdentifier().getValue());
697
		      	if (!mappingExists) {
698
		      		IdentifierManager.getInstance().createMapping(sysMeta.getIdentifier().getValue(), docid);
699
		      	}
700
				// save the system metadata
701
				HazelcastService.getInstance().getSystemMetadataMap().put(sysMeta.getIdentifier(), sysMeta);
702
				
703
			}
704
      
705
			// dates
706
			String createdDateString = docinfoHash.get("date_created");
707
			String updatedDateString = docinfoHash.get("date_updated");
708
			Date createdDate = DateTimeMarshaller.deserializeDateToUTC(createdDateString);
709
			Date updatedDate = DateTimeMarshaller.deserializeDateToUTC(updatedDateString);
710
		      
711
			logReplication.info("ReplicationService.handleForceReplicateRequest - homeServer: " + homeServer);
712
			// Get Document type
713
			String docType = (String) docinfoHash.get("doctype");
714
			logReplication.info("ReplicationService.handleForceReplicateRequest - docType: " + docType);
715
			String parserBase = null;
716
			// this for eml2 and we need user eml2 parser
717
			if (docType != null
718
					&& (docType.trim()).equals(DocumentImpl.EML2_0_0NAMESPACE)) {
719
				logReplication.warn("ReplicationService.handleForceReplicateRequest - This is an eml200 document!");
720
				parserBase = DocumentImpl.EML200;
721
			} else if (docType != null
722
					&& (docType.trim()).equals(DocumentImpl.EML2_0_1NAMESPACE)) {
723
				logReplication.warn("ReplicationService.handleForceReplicateRequest - This is an eml2.0.1 document!");
724
				parserBase = DocumentImpl.EML200;
725
			} else if (docType != null
726
					&& (docType.trim()).equals(DocumentImpl.EML2_1_0NAMESPACE)) {
727
				logReplication.warn("ReplicationService.handleForceReplicateRequest - This is an eml2.1.0 document!");
728
				parserBase = DocumentImpl.EML210;
729
			} else if (docType != null
730
					&& (docType.trim()).equals(DocumentImpl.EML2_1_1NAMESPACE)) {
731
				logReplication.warn("ReplicationService.handleForceReplicateRequest - This is an eml2.1.1 document!");
732
				parserBase = DocumentImpl.EML210;
733
			}
734
			logReplication.warn("ReplicationService.handleForceReplicateRequest - The parserBase is: " + parserBase);
735
			
736
			/*String formatId = null;
737
		    //get the format id from the system metadata 
738
		    if(sysMeta != null && sysMeta.getFormatId() != null) {
739
		          logMetacat.debug("ReplicationService.handleForceReplicateRequest - the format id will be got from the system metadata for the object "+docid);
740
		          formatId = sysMeta.getFormatId().getValue();
741
		    }*/
742
		      
743
			// Get DBConnection from pool
744
			dbConn = DBConnectionPool
745
					.getDBConnection("MetacatReplication.handleForceReplicateRequest");
746
			serialNumber = dbConn.getCheckOutSerialNumber();
747
			// write the document to local database
748
			DocumentImplWrapper wrapper = new DocumentImplWrapper(parserBase, false, false);
749
			//try this independently so we can set access even if the update action is invalid (i.e docid has not changed)
750
			try {
751
				wrapper.writeReplication(dbConn, xmldoc, xmlBytes, null, null,
752
						dbaction, docid, null, null, homeServer, server, createdDate,
753
						updatedDate);
754
			} finally {
755
				if(sysMeta != null) {
756
					// submit for indexing. When the doc writing process fails, the index process will fail as well. But this failure
757
					// will not interrupt the process.
758
					try {
759
						MetacatSolrIndex.getInstance().submit(sysMeta.getIdentifier(), sysMeta, null, true);
760
					} catch (Exception ee) {
761
						logReplication.warn("ReplicationService.handleForceReplicateRequest - couldn't index the doc since "+ee.getMessage());
762
					}
763
	                
764
				}
765
				//process extra access rules before dealing with the write exception (doc exist already)
766
				try {
767
		        	// check if we had a guid -> docid mapping
768
		        	String docidNoRev = DocumentUtil.getDocIdFromAccessionNumber(docid);
769
		        	int rev = DocumentUtil.getRevisionFromAccessionNumber(docid);
770
		        	IdentifierManager.getInstance().getGUID(docidNoRev, rev);
771
		        	// no need to create the mapping if we have it
772
		        } catch (McdbDocNotFoundException mcdbe) {
773
		        	// create mapping if we don't
774
		        	IdentifierManager.getInstance().createMapping(docid, docid);
775
		        }
776
		        Vector<XMLAccessDAO> accessControlList = dih.getAccessControlList();
777
		        if (accessControlList != null) {
778
		        	AccessControlForSingleFile acfsf = new AccessControlForSingleFile(docid);
779
		        	for (XMLAccessDAO xmlAccessDAO : accessControlList) {
780
		        		try {
781
			        		if (!acfsf.accessControlExists(xmlAccessDAO)) {
782
			        			acfsf.insertPermissions(xmlAccessDAO);
783
								logReplication.info("ReplicationService.handleForceReplicateRequest - document " + docid
784
										+ " permissions added to DB");
785
			        		}
786
		        		} catch (PermOrderException poe) {
787
		        			// this is problematic, but should not prevent us from replicating
788
		        			// see https://redmine.dataone.org/issues/2583
789
		        			String msg = "Could not insert access control for: " + docid + " Message: " + poe.getMessage();
790
		        			logMetacat.error(msg, poe);
791
		        			logReplication.error(msg, poe);
792
		        		}
793
		            }
794
		        }
795
		        
796
		        // process the real owner and updater
797
				String user = (String) docinfoHash.get("user_owner");
798
				String updated = (String) docinfoHash.get("user_updated");
799
		        updateUserOwner(dbConn, docid, user, updated);
800

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

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

    
884
		}//catch
885

    
886
	}
887

    
888
	/**
889
	 * when a forcereplication data file request comes in, local host sends a
890
	 * readdata request to the requesting server (remote server) for the specified
891
	 * docid. Then store it in local database and file system
892
	 */
893
	protected static void handleForceReplicateDataFileRequest(Hashtable<String, String[]> params,
894
			HttpServletRequest request) {
895

    
896
		//make sure there is some parameters
897
		if (params.isEmpty()) {
898
			return;
899
		}
900
		// Get remote server
901
		String server = ((String[]) params.get("server"))[0];
902
		// the docid should include rev number
903
		String docid = ((String[]) params.get("docid"))[0];
904
		// Make sure there is a docid and server
905
		if (docid == null || server == null || server.equals("")) {
906
			logMetacat.error("ReplicationService.handleForceReplicateDataFileRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
907
			logReplication.error("ReplicationService.handleForceReplicateDataFileRequest - Didn't specify docid or server for replication");
908
			return;
909
		}
910

    
911
		// Overide or not
912
		//    boolean override = false;
913
		// dbaction - update or insert
914
		String dbaction = null;
915

    
916
		try {
917
			//docid was switch to two parts uinque code and rev
918
			//String uniqueCode=MetacatUtil.getDocIdFromString(docid);
919
			//int rev=MetacatUtil.getVersionFromString(docid);
920
			if (params.containsKey("dbaction")) {
921
				dbaction = ((String[]) params.get("dbaction"))[0];
922
			} else//default value is update
923
			{
924
//				dbaction = "update";
925
				dbaction = null;
926
			}
927

    
928
			logReplication.info("ReplicationService.handleForceReplicateDataFileRequest - Force replication request from: " + server);
929
			logReplication.info("ReplicationService.handleForceReplicateDataFileRequest - Force replication docid: " + docid);
930
			logReplication.info("ReplicationService.handleForceReplicateDataFileRequest - Force replication action: " + dbaction);
931
			// get the document info from server
932
			URL docinfourl = new URL("https://" + server + "?server="
933
					+ MetacatUtil.getLocalReplicationServerName()
934
					+ "&action=getdocumentinfo&docid=" + docid);
935

    
936
			String docInfoStr = ReplicationService.getURLContent(docinfourl);
937
			
938
			// strip out the system metadata portion
939
		    String systemMetadataXML = ReplicationUtil.getSystemMetadataContent(docInfoStr);
940
		   	docInfoStr = ReplicationUtil.getContentWithoutSystemMetadata(docInfoStr);
941

    
942
			//dih is the parser for the docinfo xml format
943
			DocInfoHandler dih = new DocInfoHandler();
944
			XMLReader docinfoParser = ReplicationHandler.initParser(dih);
945
			docinfoParser.parse(new InputSource(new StringReader(docInfoStr)));
946
			Hashtable<String, String> docinfoHash = dih.getDocInfo();
947
			
948
			String docName = (String) docinfoHash.get("docname");
949

    
950
			String docType = (String) docinfoHash.get("doctype");
951

    
952
			String docHomeServer = (String) docinfoHash.get("home_server");
953
			
954
			String createdDateString = docinfoHash.get("date_created");
955
			String updatedDateString = docinfoHash.get("date_updated");
956
			
957
			Date createdDate = DateTimeMarshaller.deserializeDateToUTC(createdDateString);
958
			Date updatedDate = DateTimeMarshaller.deserializeDateToUTC(updatedDateString);
959
			
960
			logReplication.info("ReplicationService.handleForceReplicateDataFileRequest - docHomeServer of datafile: " + docHomeServer);
961

    
962
			// in case we have a write exception, we still want to track access and sysmeta
963
			Exception writeException = null;
964

    
965
			// do we need the object content?
966
			if (dbaction != null && (dbaction.equals("insert") || dbaction.equals("update"))) {
967
				//Get data file and store it into local file system.
968
				// sending back readdata request to server
969
				URL url = new URL("https://" + server + "?server="
970
						+ MetacatUtil.getLocalReplicationServerName()
971
						+ "&action=readdata&docid=" + docid);
972
				String datafilePath = PropertyService
973
						.getProperty("application.datafilepath");
974

    
975
				
976
				
977
				//register data file into xml_documents table and write data file
978
				//into file system
979
				InputStream inputStream = null;
980
				try {
981
				    inputStream = getURLStream(url);
982
					DocumentImpl.writeDataFileInReplication(inputStream,
983
							datafilePath, docName, docType, docid, null, docHomeServer,
984
							server, DocumentImpl.DOCUMENTTABLE, false, createdDate,
985
							updatedDate);
986
				} catch (Exception e) {
987
					writeException = e;
988
				} finally {
989
				    IOUtils.closeQuietly(inputStream);
990
				}
991

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

    
1050
			logReplication.info("ReplicationService.handleForceReplicateDataFileRequest - datafile " + docid + " added to DB with "
1051
					+ "action " + dbaction);
1052
			EventLog.getInstance().log(request.getRemoteAddr(), request.getHeader("User-Agent"), REPLICATIONUSER,
1053
					docid, dbaction);
1054

    
1055
		} catch (Exception e) {
1056
			e.printStackTrace();
1057
			logMetacat.error("ReplicationService.handleForceReplicateDataFileRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG, e);                         
1058
			logReplication.error("ReplicationService.handleForceReplicateDataFileRequest - Datafile " + docid
1059
					+ " failed to added to DB with " + "action " + dbaction + " because "
1060
					+ e.getMessage());
1061
			logReplication.error("ReplicationService.handleForceReplicateDataFileRequest - ERROR in MetacatReplication.handleForceDataFileReplicate"
1062
					+ "Request(): " + e.getMessage());
1063
		}
1064
	}
1065

    
1066
	/**
1067
	 * Grants or denies a lock to a requesting host.
1068
	 * The servlet parameters of interrest are:
1069
	 * docid: the docid of the file the lock is being requested for
1070
	 * currentdate: the timestamp of the document on the remote server
1071
	 *
1072
	 */
1073
	protected static void handleGetLockRequest(
1074
			Hashtable<String, String[]> params, HttpServletResponse response) {
1075

    
1076
		try {
1077

    
1078
			String docid = ((String[]) params.get("docid"))[0];
1079
			String remoteRev = ((String[]) params.get("updaterev"))[0];
1080
			DocumentImpl requestDoc = new DocumentImpl(docid);
1081
			logReplication.info("ReplicationService.handleGetLockRequest - lock request for " + docid);
1082
			int localRevInt = requestDoc.getRev();
1083
			int remoteRevInt = Integer.parseInt(remoteRev);
1084

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

    
1119
	/**
1120
	 * Sends all of the xml_documents information encoded in xml to a requestor
1121
	 * the format is:
1122
	 * <!ELEMENT documentinfo (docid, docname, doctype, doctitle, user_owner,
1123
	 *                  user_updated, home_server, public_access, rev)/>
1124
	 * all of the subelements of document info are #PCDATA
1125
	 */
1126
	protected static void handleGetDocumentInfoRequest(
1127
			Hashtable<String, String[]> params, HttpServletResponse response) {
1128
		String docid = ((String[]) (params.get("docid")))[0];
1129

    
1130
		try {
1131
			// get docinfo as XML string
1132
			String docinfoXML = getDocumentInfo(docid);
1133
			
1134
			// get a writer for sending back to response
1135
			response.setContentType("text/xml");
1136
			Writer out = response.getWriter();
1137
			out.write(docinfoXML);
1138
			out.close();
1139

    
1140
		} catch (Exception e) {
1141
			logMetacat.error("ReplicationService.handleGetDocumentInfoRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
1142
			logReplication.error("ReplicationService.handleGetDocumentInfoRequest - error in metacatReplication.handlegetdocumentinforequest "
1143
					+ "for doc: " + docid + " : " + e.getMessage());
1144
		}
1145

    
1146
	}
1147
	
1148
	public static Hashtable<String, String> getDocumentInfoMap(String docid)
1149
			throws HandlerException, AccessControlException, MarshallingException,
1150
			IOException, McdbException, SAXException {
1151
		
1152
		// Try get docid info from remote server
1153
		DocInfoHandler dih = new DocInfoHandler();
1154
		XMLReader docinfoParser = ReplicationHandler.initParser(dih);
1155

    
1156
		String docInfoStr = getDocumentInfo(docid);
1157

    
1158
		// strip out the system metadata portion
1159
		String systemMetadataXML = ReplicationUtil.getSystemMetadataContent(docInfoStr);
1160
		docInfoStr = ReplicationUtil.getContentWithoutSystemMetadata(docInfoStr);
1161

    
1162
		docinfoParser.parse(new InputSource(new StringReader(docInfoStr)));
1163
		Hashtable<String, String> docinfoHash = dih.getDocInfo();
1164

    
1165
		return docinfoHash;
1166
	}
1167
	
1168
	/**
1169
	 * Gets a docInfo XML snippet for the replication API
1170
	 * @param docid
1171
	 * @return
1172
	 * @throws AccessControlException
1173
	 * @throws JiBXException
1174
	 * @throws IOException
1175
	 * @throws McdbException
1176
	 */
1177
	public static String getDocumentInfo(String docid) throws AccessControlException, MarshallingException, IOException, McdbException {
1178
		StringBuffer sb = new StringBuffer();
1179

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

    
1222
		sb.append("<accessControl>");
1223

    
1224
		AccessControlForSingleFile acfsf = new AccessControlForSingleFile(docid); 
1225
		sb.append(acfsf.getAccessString());
1226
		
1227
		sb.append("</accessControl>");
1228

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

    
1255
		} catch (Exception e) {
1256
			String msg = "ReplicationService.handleGetSystemMetadataRequest for guid: " + guid + " : " + e.getMessage();
1257
			logMetacat.error(msg);                         
1258
			logReplication.error(msg);
1259
		}
1260

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

    
1297
		} catch (Exception e) {
1298
			logMetacat.error("ReplicationService.handleForceReplicateRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG, e);                         
1299
			logReplication.error("ReplicationService.handleForceReplicateRequest - General error when processing guid: " + guid, e);
1300
		}
1301
	}
1302

    
1303
	/**
1304
	 * Sends a datafile to a remote host
1305
	 */
1306
	protected static void handleGetDataFileRequest(OutputStream outPut,
1307
			Hashtable<String, String[]> params, HttpServletResponse response)
1308

    
1309
	{
1310
		// File path for data file
1311
		String filepath;
1312
		// Request docid
1313
		String docId = ((String[]) (params.get("docid")))[0];
1314
		//check if the doicd is null
1315
		if (docId == null) {
1316
			logMetacat.error("ReplicationService.handleGetDataFileRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
1317
			logReplication.error("ReplicationService.handleGetDataFileRequest - Didn't specify docid for replication");
1318
			return;
1319
		}
1320

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

    
1342
		if (!filepath.endsWith("/")) {
1343
			filepath += "/";
1344
		}
1345
		// Get file aboslute file name
1346
		String filename = filepath + docId;
1347

    
1348
		//MIME type
1349
		String contentType = null;
1350
		if (filename.endsWith(".xml")) {
1351
			contentType = "text/xml";
1352
		} else if (filename.endsWith(".css")) {
1353
			contentType = "text/css";
1354
		} else if (filename.endsWith(".dtd")) {
1355
			contentType = "text/plain";
1356
		} else if (filename.endsWith(".xsd")) {
1357
			contentType = "text/xml";
1358
		} else if (filename.endsWith("/")) {
1359
			contentType = "text/html";
1360
		} else {
1361
			File f = new File(filename);
1362
			if (f.isDirectory()) {
1363
				contentType = "text/html";
1364
			} else {
1365
				contentType = "application/octet-stream";
1366
			}
1367
		}
1368

    
1369
		// Set the mime type
1370
		response.setContentType(contentType);
1371

    
1372
		// Get the content of the file
1373
		FileInputStream fin = null;
1374
		try {
1375
			// FileInputStream to metacat
1376
			fin = new FileInputStream(filename);
1377
			// 4K buffer
1378
			byte[] buf = new byte[4 * 1024];
1379
			// Read data from file input stream to byte array
1380
			int b = fin.read(buf);
1381
			// Write to outStream from byte array
1382
			while (b != -1) {
1383
				outPut.write(buf, 0, b);
1384
				b = fin.read(buf);
1385
			}
1386
			// close file input stream
1387
			fin.close();
1388

    
1389
		} catch (Exception e) {
1390
			logMetacat.error("ReplicationService.handleGetDataFileRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
1391
			logReplication.error("ReplicationService.handleGetDataFileRequest - error getting data file from MetacatReplication."
1392
					+ "handlGetDataFileRequest " + e.getMessage());
1393
			e.printStackTrace(System.out);
1394
		} finally {
1395
		    IOUtils.closeQuietly(fin);
1396
		}
1397

    
1398
	}
1399

    
1400
	/**
1401
	 * Sends a document to a remote host
1402
	 */
1403
	protected static void handleGetDocumentRequest(
1404
			Hashtable<String, String[]> params, HttpServletResponse response) {
1405

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

    
1430
			String docid = params.get("docid")[0];
1431
			logReplication.debug("ReplicationService.handleGetDocumentRequest - MetacatReplication.handleGetDocumentRequest for docid: "
1432
					+ docid);
1433
			DocumentImpl di = new DocumentImpl(docid);
1434

    
1435
			String documentDir = PropertyService
1436
					.getProperty("application.documentfilepath");
1437
			documentPath = documentDir + FileUtil.getFS() + docid;
1438

    
1439
			// if the document does not exist on disk, read it from db and write
1440
			// it to disk.
1441
			if (FileUtil.getFileStatus(documentPath) == FileUtil.DOES_NOT_EXIST
1442
					|| FileUtil.getFileSize(documentPath) == 0) {
1443
				fos = new FileOutputStream(documentPath);
1444
				is = di.toXml(fos, null, null, true);
1445
				fos.close();
1446
				is.close();
1447
			}
1448

    
1449
			// read the file from disk and send it to outputstream
1450
			outputStream = response.getOutputStream();
1451
			is = di.readFromFileSystem(outputStream, null, null, documentPath);
1452
			is.close();
1453
			outputStream.close();
1454

    
1455
			logReplication.info("ReplicationService.handleGetDocumentRequest - document " + docid + " sent");
1456

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

    
1525
	}
1526

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

    
1551
		try {
1552
			// get writer, TODO: encoding?
1553
			response.setContentType("text/xml");
1554
			out = response.getWriter();
1555
			
1556
			// Check out a DBConnection from pool
1557
			dbConn = DBConnectionPool
1558
					.getDBConnection("MetacatReplication.handleUpdateRequest");
1559
			serialNumber = dbConn.getCheckOutSerialNumber();
1560
			// Create a server list from xml_replication table
1561
			serverList = new ReplicationServerList();
1562

    
1563
			// Get remote server name from param
1564
			String server = ((String[]) params.get("server"))[0];
1565
			// If no servr name in param, return a error
1566
			if (server == null || server.equals("")) {
1567
				out.write("<error>Request didn't specify server name</error>");
1568
				out.close();
1569
				return;
1570
			}//if
1571

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

    
1589
			// Check if local host configure to replicate xml documents to remote
1590
			// server. If not send back a error message
1591
			if (!serverList.getReplicationValue(server)) {
1592
				out.write("<error>Configuration not allow to replicate document to you</error>");
1593
				out.close();
1594
				return;
1595
			}//if
1596

    
1597
			// Store the sql command
1598
			StringBuffer docsql = new StringBuffer();
1599
			StringBuffer revisionSql = new StringBuffer();
1600
			
1601
			// Store the data set file
1602
			Vector<Vector<String>> packageFiles = new Vector<Vector<String>>();
1603

    
1604
			// Append local server's name and replication servlet to doclist
1605
			out.append("<?xml version=\"1.0\"?><replication>");
1606
			out.append("<server>")
1607
					.append(MetacatUtil.getLocalReplicationServerName());
1608
			//doclist.append(util.getProperty("replicationpath"));
1609
			out.append("</server><updates>");
1610

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

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

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

    
1714
			pstmt = dbConn.prepareStatement(delsql.toString());
1715
			//usage count should increas 1
1716
			dbConn.increaseUsageCount(1);
1717

    
1718
			pstmt.execute();
1719
			rs = pstmt.getResultSet();
1720
			tablehasrows = rs.next();
1721
			while (tablehasrows) { //handle the deleted documents
1722
				out.append("<deletedDocument><docid>").append(rs.getString(1));
1723
				out.append("</docid><rev></rev></deletedDocument>");
1724
				//note that rev is always empty for deleted docs
1725
				tablehasrows = rs.next();
1726
			}
1727

    
1728
			//now we can put the package files into the xml results
1729
			for (int i = 0; i < packageFiles.size(); i++) {
1730
				Vector<String> v = packageFiles.elementAt(i);
1731
				out.append("<updatedDocument>");
1732
				out.append("<docid>").append(v.elementAt(0));
1733
				out.append("</docid><rev>");
1734
				out.append(v.elementAt(1));
1735
				out.append("</rev>");
1736
				out.append("</updatedDocument>");
1737
			}
1738
			// add revision doc list  
1739
			out.append(prepareRevisionDoc(dbConn, revisionSql.toString(),
1740
					replicateData));
1741

    
1742
			out.append("</updates></replication>");
1743
			logReplication.info("ReplicationService.handleUpdateRequest - done writing to output stream.");
1744
			pstmt.close();
1745
			//conn.close();
1746

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

    
1776
	}//handlUpdateRequest
1777

    
1778
	/**
1779
	 * 
1780
	 * @param dbConn connection for doing the update
1781
	 * @param docid the document id to update
1782
	 * @param owner the user_owner
1783
	 * @param updater the user_updated
1784
	 * @throws SQLException
1785
	 */
1786
	public static void updateUserOwner(DBConnection dbConn, String docid, String owner, String updater) throws SQLException {
1787
	
1788
		String sql = 
1789
			"UPDATE xml_documents " +
1790
			"SET user_owner = ?, " +
1791
			"user_updated = ? " +
1792
			"WHERE docid = ?;";
1793
		PreparedStatement pstmt = dbConn.prepareStatement(sql);
1794
		//usage count should increas 1
1795
		dbConn.increaseUsageCount(1);
1796

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

    
1819
		pstmt.execute();
1820
		ResultSet rs = pstmt.getResultSet();
1821
		logReplication.warn("Processing replication revision for documents");
1822
		while (rs.next()) {
1823
			String recordDoctype = rs.getString(3);
1824

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

    
1829
				// do nothing
1830
			} else {
1831
				String docid = rs.getString(1);
1832
				int rev = rs.getInt(2);
1833
				logMetacat.debug("Processing replication revision for docid: " + docid + "." + rev);
1834

    
1835
				revDocList.append("<revisionDocument>");
1836
				revDocList.append("<docid>").append(docid);
1837
				revDocList.append("</docid><rev>").append(rev);
1838
				revDocList.append("</rev>");
1839
				// data file
1840
				if (recordDoctype.equals("BIN")) {
1841
					revDocList.append("<datafile>");
1842
					revDocList.append(DATA_FILE_FLAG);
1843
					revDocList.append("</datafile>");
1844
				}
1845
				revDocList.append("</revisionDocument>");
1846

    
1847
			}//else
1848
		}
1849
		//System.out.println("The revision list is"+ revDocList.toString());
1850
		return revDocList.toString();
1851
	}
1852

    
1853
	/**
1854
	 * Returns the xml_catalog table encoded in xml
1855
	 */
1856
	public static String getCatalogXML() {
1857
		return handleGetCatalogRequest(null, null, false);
1858
	}
1859

    
1860
	/**
1861
	 * Sends the contents of the xml_catalog table encoded in xml
1862
	 * The xml format is:
1863
	 * <!ELEMENT xml_catalog (row*)>
1864
	 * <!ELEMENT row (entry_type, source_doctype, target_doctype, public_id,
1865
	 *                system_id)>
1866
	 * All of the sub elements of row are #PCDATA
1867

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

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

    
1953
		return null;
1954
	}
1955

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

    
1980
	/**
1981
	 * this method handles the timeout for a file lock.  when a lock is
1982
	 * granted it is granted for 30 seconds.  When this thread runs out
1983
	 * it deletes the docid from the queue, thus eliminating the lock.
1984
	 */
1985
	public void run() {
1986
		try {
1987
			logReplication.info("ReplicationService.run - thread started for docid: "
1988
					+ (String) fileLocks.elementAt(0));
1989

    
1990
			Thread.sleep(30000); //the lock will expire in 30 seconds
1991
			logReplication.info("thread for docid: "
1992
					+ (String) fileLocks.elementAt(fileLocks.size() - 1) + " exiting.");
1993

    
1994
			fileLocks.remove(fileLocks.size() - 1);
1995
			//fileLocks is treated as a FIFO queue.  If there are more than one lock
1996
			//in the vector, the first one inserted will be removed.
1997
		} catch (Exception e) {
1998
			logMetacat.error("ReplicationService.run - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
1999
			logReplication.error("ReplicationService.run - error in file lock thread from "
2000
					+ "MetacatReplication.run: " + e.getMessage());
2001
		}
2002
	}
2003

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

    
2031
			//conn.close();
2032
		} catch (Exception e) {
2033
			logMetacat.error("ReplicationService.getServerNameForServerCode - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
2034
			logReplication.error("ReplicationService.getServerNameForServerCode - Error in MetacatReplication.getServer: " + e.getMessage());
2035
		} finally {
2036
			try {
2037
				pstmt.close();
2038
			}//try
2039
			catch (SQLException ee) {
2040
				logMetacat.error("ReplicationService.getServerNameForServerCode - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
2041
				logReplication.error("ReplicationService.getServerNameForServerCode - Error in MetacactReplication.getserver: "
2042
						+ ee.getMessage());
2043
			}//catch
2044
			finally {
2045
				DBConnectionPool.returnDBConnection(dbConn, serialNumber);
2046
			}//fianlly
2047
		}//finally
2048

    
2049
		return null;
2050
		//return null if the server does not exist
2051
	}
2052

    
2053
	/**
2054
	 * Returns a server code given a server name
2055
	 * @param server the name of the server
2056
	 * @return integer > 0 representing the code of the server, 0 if the server
2057
	 *  does not exist.
2058
	 */
2059
	public static int getServerCodeForServerName(String server) throws ServiceException {
2060
		DBConnection dbConn = null;
2061
		int serialNumber = -1;
2062
		PreparedStatement pstmt = null;
2063
		int serverCode = 0;
2064

    
2065
		try {
2066

    
2067
			//conn = util.openDBConnection();
2068
			dbConn = DBConnectionPool.getDBConnection("MetacatReplication.getServerCode");
2069
			serialNumber = dbConn.getCheckOutSerialNumber();
2070
			pstmt = dbConn.prepareStatement("SELECT serverid FROM xml_replication "
2071
					+ "WHERE server LIKE ?");
2072
			pstmt.setString(1, server);
2073
			pstmt.execute();
2074
			ResultSet rs = pstmt.getResultSet();
2075
			boolean tablehasrows = rs.next();
2076
			if (tablehasrows) {
2077
				serverCode = rs.getInt(1);
2078
				pstmt.close();
2079
				//conn.close();
2080
				return serverCode;
2081
			}
2082

    
2083
		} catch (SQLException sqle) {
2084
			throw new ServiceException("ReplicationService.getServerCodeForServerName - " 
2085
					+ "SQL error when getting server code: " + sqle.getMessage());
2086

    
2087
		} finally {
2088
			try {
2089
				pstmt.close();
2090
				//conn.close();
2091
			}//try
2092
			catch (Exception ee) {
2093
				logMetacat.error("ReplicationService.getServerCodeForServerName - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
2094
				logReplication.error("ReplicationService.getServerNameForServerCode - Error in MetacatReplicatio.getServerCode: "
2095
						+ ee.getMessage());
2096

    
2097
			}//catch
2098
			finally {
2099
				DBConnectionPool.returnDBConnection(dbConn, serialNumber);
2100
			}//finally
2101
		}//finally
2102

    
2103
		return serverCode;
2104
	}
2105
	
2106
	/**
2107
	 * Returns a Map of serverCode=serverName 
2108
	 * @return Map of server codes to names (URIs)
2109
	 */
2110
	public static Map<Integer, String> getServerCodes() throws ServiceException {
2111
		DBConnection dbConn = null;
2112
		int serialNumber = -1;
2113
		PreparedStatement pstmt = null;
2114
		
2115
		Map<Integer, String> codes = new HashMap<Integer, String>();
2116

    
2117
		try {
2118

    
2119
			dbConn = DBConnectionPool.getDBConnection("MetacatReplication.getServerCodes");
2120
			serialNumber = dbConn.getCheckOutSerialNumber();
2121
			pstmt = dbConn.prepareStatement("SELECT serverid, server FROM xml_replication ");
2122
			pstmt.execute();
2123
			ResultSet rs = pstmt.getResultSet();
2124
			while (rs.next()) {
2125
				int serverCode = rs.getInt(1);
2126
				String server = rs.getString(2);
2127
				codes.put(serverCode, server);
2128
			}
2129
			pstmt.close();
2130
			
2131
		} catch (SQLException sqle) {
2132
			throw new ServiceException("ReplicationService.getServerCodes - " 
2133
					+ "SQL error when getting server map: " + sqle.getMessage());
2134

    
2135
		} finally {
2136
			try {
2137
				pstmt.close();
2138
			}//try
2139
			catch (Exception ee) {
2140
				logMetacat.error("ReplicationService.getServerCodes - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
2141
				logReplication.error("ReplicationService.getServerCodes - Error in MetacatReplicatio.getServerCodes: "
2142
						+ ee.getMessage());
2143

    
2144
			}//catch
2145
			finally {
2146
				DBConnectionPool.returnDBConnection(dbConn, serialNumber);
2147
			}//finally
2148
		}//finally
2149

    
2150
		return codes;
2151
	}
2152

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

    
2193
				String server = rs.getString(1);
2194
				String last_checked = rs.getString(2);
2195
				if (!server.equals("localhost")) {
2196
					sl.put(server, last_checked);
2197
				}
2198

    
2199
			} else {
2200
				pstmt.close();
2201
				//ut.returnConnection(conn);
2202
				return null;
2203
			}
2204
			pstmt.close();
2205
		} catch (Exception e) {
2206
			logMetacat.error("ReplicationService.getHomeServerInfoForDocId - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
2207
			logReplication.error("ReplicationService.getHomeServerInfoForDocId - error in replicationHandler.getHomeServer(): "
2208
					+ e.getMessage());
2209
		} finally {
2210
			try {
2211
				pstmt.close();
2212
				//ut.returnConnection(conn);
2213
			} catch (Exception ee) {
2214
				logMetacat.error("ReplicationService.getHomeServerInfoForDocId - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
2215
				logReplication.error("ReplicationService.getHomeServerInfoForDocId - Eror irn rplicationHandler.getHomeServer() "
2216
						+ "to close pstmt: " + ee.getMessage());
2217
			} finally {
2218
				DBConnectionPool.returnDBConnection(dbConn, serialNumber);
2219
			}
2220

    
2221
		}//finally
2222
		return sl;
2223
	}
2224

    
2225
	/**
2226
	 * Returns a home server location  given a accnum
2227
	 * @param accNum , given accNum for a document
2228
	 *
2229
	 */
2230
	public static int getHomeServerCodeForDocId(String accNum) throws ServiceException {
2231
		DBConnection dbConn = null;
2232
		int serialNumber = -1;
2233
		PreparedStatement pstmt = null;
2234
		int serverCode = 1;
2235
		String docId = DocumentUtil.getDocIdFromString(accNum);
2236

    
2237
		try {
2238

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

    
2264
		} catch (SQLException sqle) {
2265
			throw new ServiceException("ReplicationService.getHomeServerCodeForDocId - " 
2266
					+ "SQL error when getting home server code for docid: " + docId + " : " 
2267
					+ sqle.getMessage());
2268

    
2269
		} finally {
2270
			try {
2271
				pstmt.close();
2272
				//conn.close();
2273

    
2274
			} catch (SQLException sqle) {
2275
				logMetacat.error("ReplicationService.getHomeServerCodeForDocId - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
2276
				logReplication.error("ReplicationService.getHomeServerCodeForDocId - ReplicationService.getHomeServerCodeForDocId - " 
2277
						+ "SQL error when getting home server code for docid: " + docId + " : " 
2278
						+ sqle.getMessage());
2279
			} finally {
2280
				DBConnectionPool.returnDBConnection(dbConn, serialNumber);
2281
			}//finally
2282
		}//finally
2283
		//return serverCode;
2284
	}
2285

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

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

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

    
2414
	
2415

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

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

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

    
2515
}
(6-6/7)