Project

General

Profile

1
/**
2
 *  '$RCSfile$'
3
 *    Purpose: A Class that implements replication for metacat
4
 *  Copyright: 2000 Regents of the University of California and the
5
 *             National Center for Ecological Analysis and Synthesis
6
 *    Authors: Chad Berkley
7
 *
8
 *   '$Author: leinfelder $'
9
 *     '$Date: 2015-02-06 09:21:40 -0800 (Fri, 06 Feb 2015) $'
10
 * '$Revision: 9096 $'
11
 *
12
 * This program is free software; you can redistribute it and/or modify
13
 * it under the terms of the GNU General Public License as published by
14
 * the Free Software Foundation; either version 2 of the License, or
15
 * (at your option) any later version.
16
 *
17
 * This program is distributed in the hope that it will be useful,
18
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20
 * GNU General Public License for more details.
21
 *
22
 * You should have received a copy of the GNU General Public License
23
 * along with this program; if not, write to the Free Software
24
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
25
 */
26

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

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

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

    
62
import org.apache.commons.io.IOUtils;
63
import org.apache.http.conn.scheme.Scheme;
64
import org.apache.http.conn.ssl.SSLSocketFactory;
65
import org.apache.log4j.Logger;
66
import org.dataone.client.auth.CertificateManager;
67
import org.dataone.client.rest.HttpMultipartRestClient;
68
import org.dataone.service.types.v1.Identifier;
69
import org.dataone.service.types.v2.SystemMetadata;
70
import org.dataone.service.util.DateTimeMarshaller;
71
import org.dataone.service.util.TypeMarshaller;
72
import org.jibx.runtime.JiBXException;
73
import org.xml.sax.InputSource;
74
import org.xml.sax.SAXException;
75
import org.xml.sax.XMLReader;
76

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

    
111
public class ReplicationService extends BaseService {
112

    
113
	private static ReplicationService replicationService = null;
114

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

    
131
	public static final String REPLICATION_LOG_FILE_NAME = "metacatreplication.log";
132

    
133
	private static String DATA_FILE_FLAG = null;
134
	public static String METACAT_REPL_ERROR_MSG = null;
135
	private static Logger logReplication = Logger.getLogger("ReplicationLogging");
136
	private static Logger logMetacat = Logger.getLogger(ReplicationService.class);
137

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

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

    
180
			String timeIntervalStr = 
181
				PropertyService.getProperty("replication.timedreplicationinterval");
182
			timeInterval = (new Long(timeIntervalStr)).longValue();
183
			logReplication.info("ReplicationService.initialize - The timed replication time Interval is " + timeInterval);
184

    
185
			String firstTimeStr = 
186
				PropertyService.getProperty("replication.firsttimedreplication");
187
			logReplication.info("ReplicationService.initialize - first replication time form property is " + firstTimeStr);
188
			firstTime = ReplicationHandler.combinateCurrentDateAndGivenTime(firstTimeStr);
189

    
190
			logReplication.info("ReplicationService.initialize - After combine current time, the real first time is "
191
					+ firstTime.toString() + " minisec");
192

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

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

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

    
224
	public boolean refreshable() {
225
		return true;
226
	}
227

    
228
	protected void doRefresh() throws ServiceException {
229
		return;
230
	}
231
	
232
	public void stop() throws ServiceException{
233
		
234
	}
235

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

    
247
	      logReplication.info("ReplicationService.stopReplication - deltaT handler stopped");
248
		return;
249
	}
250
	
251
	public void startReplication(Hashtable<String, String[]> params) throws ServiceException {
252

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

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

    
347
			// add server to server list
348
			if (subaction.equals("add")) {
349
				replicate = ((String[]) params.get("replicate"))[0];
350
				server = ((String[]) params.get("server"))[0];
351

    
352
				//Get data replication value
353
				dataReplicate = ((String[]) params.get("datareplicate"))[0];
354
				
355
				//Get hub value
356
				hub = ((String[]) params.get("hub"))[0];
357

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

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

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

    
585
				tablehasrows = rs.next();
586
			}
587
			out.write("</table></body></html>");
588
			
589
			
590
			pstmt.close();
591
			//conn.close();
592

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

    
619
	}
620

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

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

    
655
			// get the document info from server
656
			URL docinfourl = new URL("https://" + server + "?server="
657
					+ MetacatUtil.getLocalReplicationServerName()
658
					+ "&action=getdocumentinfo&docid=" + docid);
659
			
660

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

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

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

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

    
866
		}//catch
867

    
868
	}
869

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

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

    
893
		// Overide or not
894
		//    boolean override = false;
895
		// dbaction - update or insert
896
		String dbaction = null;
897

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

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

    
918
			String docInfoStr = ReplicationService.getURLContent(docinfourl);
919
			
920
			// strip out the system metadata portion
921
		    String systemMetadataXML = ReplicationUtil.getSystemMetadataContent(docInfoStr);
922
		   	docInfoStr = ReplicationUtil.getContentWithoutSystemMetadata(docInfoStr);
923

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

    
932
			String docType = (String) docinfoHash.get("doctype");
933

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

    
944
			// in case we have a write exception, we still want to track access and sysmeta
945
			Exception writeException = null;
946

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

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

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

    
1028
			logReplication.info("ReplicationService.handleForceReplicateDataFileRequest - datafile " + docid + " added to DB with "
1029
					+ "action " + dbaction);
1030
			EventLog.getInstance().log(request.getRemoteAddr(), request.getHeader("User-Agent"), REPLICATIONUSER,
1031
					docid, dbaction);
1032

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

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

    
1054
		try {
1055

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

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

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

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

    
1118
		} catch (Exception e) {
1119
			logMetacat.error("ReplicationService.handleGetDocumentInfoRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG);                         
1120
			logReplication.error("ReplicationService.handleGetDocumentInfoRequest - error in metacatReplication.handlegetdocumentinforequest "
1121
					+ "for doc: " + docid + " : " + e.getMessage());
1122
		}
1123

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

    
1134
		String docInfoStr = getDocumentInfo(docid);
1135

    
1136
		// strip out the system metadata portion
1137
		String systemMetadataXML = ReplicationUtil.getSystemMetadataContent(docInfoStr);
1138
		docInfoStr = ReplicationUtil.getContentWithoutSystemMetadata(docInfoStr);
1139

    
1140
		docinfoParser.parse(new InputSource(new StringReader(docInfoStr)));
1141
		Hashtable<String, String> docinfoHash = dih.getDocInfo();
1142

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

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

    
1200
		sb.append("<accessControl>");
1201

    
1202
		AccessControlForSingleFile acfsf = new AccessControlForSingleFile(docid); 
1203
		sb.append(acfsf.getAccessString());
1204
		
1205
		sb.append("</accessControl>");
1206

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

    
1233
		} catch (Exception e) {
1234
			String msg = "ReplicationService.handleGetSystemMetadataRequest for guid: " + guid + " : " + e.getMessage();
1235
			logMetacat.error(msg);                         
1236
			logReplication.error(msg);
1237
		}
1238

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

    
1275
		} catch (Exception e) {
1276
			logMetacat.error("ReplicationService.handleForceReplicateRequest - " + ReplicationService.METACAT_REPL_ERROR_MSG, e);                         
1277
			logReplication.error("ReplicationService.handleForceReplicateRequest - General error when processing guid: " + guid, e);
1278
		}
1279
	}
1280

    
1281
	/**
1282
	 * Sends a datafile to a remote host
1283
	 */
1284
	protected static void handleGetDataFileRequest(OutputStream outPut,
1285
			Hashtable<String, String[]> params, HttpServletResponse response)
1286

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

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

    
1320
		if (!filepath.endsWith("/")) {
1321
			filepath += "/";
1322
		}
1323
		// Get file aboslute file name
1324
		String filename = filepath + docId;
1325

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

    
1347
		// Set the mime type
1348
		response.setContentType(contentType);
1349

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

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

    
1376
	}
1377

    
1378
	/**
1379
	 * Sends a document to a remote host
1380
	 */
1381
	protected static void handleGetDocumentRequest(
1382
			Hashtable<String, String[]> params, HttpServletResponse response) {
1383

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

    
1408
			String docid = params.get("docid")[0];
1409
			logReplication.debug("ReplicationService.handleGetDocumentRequest - MetacatReplication.handleGetDocumentRequest for docid: "
1410
					+ docid);
1411
			DocumentImpl di = new DocumentImpl(docid);
1412

    
1413
			String documentDir = PropertyService
1414
					.getProperty("application.documentfilepath");
1415
			documentPath = documentDir + FileUtil.getFS() + docid;
1416

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

    
1427
			// read the file from disk and send it to outputstream
1428
			outputStream = response.getOutputStream();
1429
			is = di.readFromFileSystem(outputStream, null, null, documentPath);
1430
			is.close();
1431
			outputStream.close();
1432

    
1433
			logReplication.info("ReplicationService.handleGetDocumentRequest - document " + docid + " sent");
1434

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

    
1503
	}
1504

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

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

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

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

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

    
1575
			// Store the sql command
1576
			StringBuffer docsql = new StringBuffer();
1577
			StringBuffer revisionSql = new StringBuffer();
1578
			
1579
			// Store the data set file
1580
			Vector<Vector<String>> packageFiles = new Vector<Vector<String>>();
1581

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

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

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

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

    
1692
			pstmt = dbConn.prepareStatement(delsql.toString());
1693
			//usage count should increas 1
1694
			dbConn.increaseUsageCount(1);
1695

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

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

    
1720
			out.append("</updates></replication>");
1721
			logReplication.info("ReplicationService.handleUpdateRequest - done writing to output stream.");
1722
			pstmt.close();
1723
			//conn.close();
1724

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

    
1754
	}//handlUpdateRequest
1755

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

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

    
1797
		pstmt.execute();
1798
		ResultSet rs = pstmt.getResultSet();
1799
		logReplication.warn("Processing replication revision for documents");
1800
		while (rs.next()) {
1801
			String recordDoctype = rs.getString(3);
1802

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

    
1807
				// do nothing
1808
			} else {
1809
				String docid = rs.getString(1);
1810
				int rev = rs.getInt(2);
1811
				logMetacat.debug("Processing replication revision for docid: " + docid + "." + rev);
1812

    
1813
				revDocList.append("<revisionDocument>");
1814
				revDocList.append("<docid>").append(docid);
1815
				revDocList.append("</docid><rev>").append(rev);
1816
				revDocList.append("</rev>");
1817
				// data file
1818
				if (recordDoctype.equals("BIN")) {
1819
					revDocList.append("<datafile>");
1820
					revDocList.append(DATA_FILE_FLAG);
1821
					revDocList.append("</datafile>");
1822
				}
1823
				revDocList.append("</revisionDocument>");
1824

    
1825
			}//else
1826
		}
1827
		//System.out.println("The revision list is"+ revDocList.toString());
1828
		return revDocList.toString();
1829
	}
1830

    
1831
	/**
1832
	 * Returns the xml_catalog table encoded in xml
1833
	 */
1834
	public static String getCatalogXML() {
1835
		return handleGetCatalogRequest(null, null, false);
1836
	}
1837

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

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

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

    
1931
		return null;
1932
	}
1933

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

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

    
1968
			Thread.sleep(30000); //the lock will expire in 30 seconds
1969
			logReplication.info("thread for docid: "
1970
					+ (String) fileLocks.elementAt(fileLocks.size() - 1) + " exiting.");
1971

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

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

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

    
2027
		return null;
2028
		//return null if the server does not exist
2029
	}
2030

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

    
2043
		try {
2044

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

    
2061
		} catch (SQLException sqle) {
2062
			throw new ServiceException("ReplicationService.getServerCodeForServerName - " 
2063
					+ "SQL error when getting server code: " + sqle.getMessage());
2064

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

    
2075
			}//catch
2076
			finally {
2077
				DBConnectionPool.returnDBConnection(dbConn, serialNumber);
2078
			}//finally
2079
		}//finally
2080

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

    
2095
		try {
2096

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

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

    
2122
			}//catch
2123
			finally {
2124
				DBConnectionPool.returnDBConnection(dbConn, serialNumber);
2125
			}//finally
2126
		}//finally
2127

    
2128
		return codes;
2129
	}
2130

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

    
2171
				String server = rs.getString(1);
2172
				String last_checked = rs.getString(2);
2173
				if (!server.equals("localhost")) {
2174
					sl.put(server, last_checked);
2175
				}
2176

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

    
2199
		}//finally
2200
		return sl;
2201
	}
2202

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

    
2215
		try {
2216

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

    
2242
		} catch (SQLException sqle) {
2243
			throw new ServiceException("ReplicationService.getHomeServerCodeForDocId - " 
2244
					+ "SQL error when getting home server code for docid: " + docId + " : " 
2245
					+ sqle.getMessage());
2246

    
2247
		} finally {
2248
			try {
2249
				pstmt.close();
2250
				//conn.close();
2251

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

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

    
2338
		SSLSocketFactory socketFactory = null;
2339
		try {
2340

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

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

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

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

    
2461
}
(6-6/7)