// Copyright 2009 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.enterprise.connector.util.diffing;
import com.google.enterprise.connector.spi.Document;
import com.google.enterprise.connector.spi.DocumentList;
import com.google.enterprise.connector.spi.RepositoryException;
import com.google.enterprise.connector.spi.TraversalContext;
import com.google.enterprise.connector.spi.TraversalContextAware;
import com.google.enterprise.connector.spi.TraversalManager;
import com.google.enterprise.connector.spi.TraversalSchedule;
import com.google.enterprise.connector.spi.TraversalScheduleAware;
import java.io.IOException;
import java.util.Map;
import java.util.logging.Logger;
/**
* Implementation of {@link TraversalManager} for the {@link DiffingConnector}.
*
* @since 2.8
*/
public class DiffingConnectorTraversalManager implements TraversalManager,
TraversalContextAware, TraversalScheduleAware {
private static final Logger LOG = Logger.getLogger(
DiffingConnectorTraversalManager.class.getName());
private final DocumentSnapshotRepositoryMonitorManager
snapshotRepositoryMonitorManager;
private final TraversalContextManager traversalContextManager;
/**
* Boolean to mark TraversalManager as invalid.
* It's possible for Connector Manager to keep a reference to
* an outdated TraversalManager (after a new one has been given
* previous TraversalManagers are invalid to use).
*/
private boolean isActive = true;
/**
* Creates a {@link DiffingConnectorTraversalManager}.
*
* @param snapshotRepositoryMonitorManager the
* {@link DocumentSnapshotRepositoryMonitorManager}
* for use accessing a {@link ChangeSource}
* @param traversalContextManager {@link TraversalContextManager}
* that holds the current {@link TraversalContext}
*/
public DiffingConnectorTraversalManager(
DocumentSnapshotRepositoryMonitorManager snapshotRepositoryMonitorManager,
TraversalContextManager traversalContextManager) {
this.snapshotRepositoryMonitorManager = snapshotRepositoryMonitorManager;
this.traversalContextManager = traversalContextManager;
}
private DocumentList newDocumentList(String checkpoint)
throws RepositoryException {
CheckpointAndChangeQueue checkpointAndChangeQueue =
snapshotRepositoryMonitorManager.getCheckpointAndChangeQueue();
try {
DiffingConnectorDocumentList documentList = new DiffingConnectorDocumentList(
checkpointAndChangeQueue,
CheckpointAndChangeQueue.initializeCheckpointStringIfNull(
checkpoint));
Map<String, MonitorCheckpoint> guaranteesMade =
checkpointAndChangeQueue.getMonitorRestartPoints();
snapshotRepositoryMonitorManager.acceptGuarantees(guaranteesMade);
return new ConfirmActiveDocumentList(documentList);
} catch (IOException e) {
throw new RepositoryException("Failure when making DocumentList.", e);
}
}
@Override
public synchronized void setBatchHint(int batchHint) {
if (isActive()) {
snapshotRepositoryMonitorManager.getCheckpointAndChangeQueue()
.setMaximumQueueSize(batchHint);
}
}
/** Start document crawling and piping as if from beginning. */
@Override
public synchronized DocumentList startTraversal() throws RepositoryException {
if (isActive()) {
// Entirely reset connector's state.
snapshotRepositoryMonitorManager.stop();
snapshotRepositoryMonitorManager.clean();
// With no state issue crawl command from null (beginning) checkpoint.
return resumeTraversal(null);
} else {
throw new RepositoryException(
"Inactive FileTraversalManager referanced.");
}
}
@Override
public synchronized DocumentList resumeTraversal(String checkpoint)
throws RepositoryException {
/* Exhaustive list of method's use:
resumeTraversal(null) from startTraversal:
monitors get started from null
resumeTraversal(null) from Connector Manager sometime after startTraversal:
monitors already started from previous resumeTraversal call
resumeTraversal(cp) from Connector Manager without a startTraversal:
means there was a shutdown or turn off
monitors get started from cp; should use state
resumeTraversal(cp) from Connector Manager sometime after some uses:
is most common case; roll
*/
if (isActive()) {
if (!snapshotRepositoryMonitorManager.isRunning()) {
snapshotRepositoryMonitorManager.start(checkpoint);
}
return newDocumentList(checkpoint);
} else {
throw new RepositoryException(
"Inactive FileTraversalManager referanced.");
}
}
@Override
public synchronized void setTraversalContext(
TraversalContext traversalContext) {
if (isActive()) {
this.traversalContextManager.setTraversalContext(traversalContext);
}
}
@Override
public synchronized void setTraversalSchedule(TraversalSchedule
traversalSchedule) {
snapshotRepositoryMonitorManager.setTraversalSchedule(traversalSchedule);
}
public synchronized void deactivate() {
isActive = false;
snapshotRepositoryMonitorManager.stop();
}
/** Public for testing. */
public synchronized boolean isActive() {
if (!isActive) {
LOG.info("Inactive FileTraversalManager referanced.");
}
return isActive;
}
/**
* A delegating {@link DocumentList} that throws an {@link Exception}
* if called when {@link DiffingConnectorTraversalManager#isActive()} returns
* false.
*/
private class ConfirmActiveDocumentList implements DocumentList{
DocumentList delegate;
private ConfirmActiveDocumentList(DocumentList delegate) {
this.delegate = delegate;
}
/**
* @throws RepositoryException if {@link DiffingConnectorTraversalManager#isActive()}
* returns false.
*/
@Override
public String checkpoint() throws RepositoryException {
synchronized (DiffingConnectorTraversalManager.this) {
if (isActive()) {
return delegate.checkpoint();
} else {
throw new RepositoryException(
"Inactive FileTraversalManager referanced.");
}
}
}
/**
* @throws RepositoryException if {@link DiffingConnectorTraversalManager#isActive()}
* returns false.
*/
@Override
public Document nextDocument() throws RepositoryException {
synchronized (DiffingConnectorTraversalManager.this) {
if (isActive()) {
return delegate.nextDocument();
} else {
throw new RepositoryException(
"Inactive FileTraversalManager referanced.");
}
}
}
}
}