/* See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* Esri Inc. licenses this file to You 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.esri.gpt.catalog.arcgis.metadata;
import com.esri.gpt.catalog.arcims.ImsServiceException;
import com.esri.gpt.catalog.context.CatalogIndexException;
import com.esri.gpt.catalog.lucene.LuceneIndexAdapter;
import com.esri.gpt.catalog.publication.ProcessingContext;
import com.esri.gpt.catalog.publication.PublicationRecord;
import com.esri.gpt.framework.collection.StringAttributeMap;
import com.esri.gpt.framework.context.RequestContext;
import com.esri.gpt.framework.http.HttpClientRequest;
import com.esri.gpt.framework.scheduler.IScheduledTask;
import com.esri.gpt.framework.security.principal.Publisher;
import com.esri.gpt.framework.util.Val;
import java.io.IOException;
import java.sql.SQLException;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.lucene.store.Lock;
import org.apache.lucene.store.LockObtainFailedException;
/**
* Background thread to synchronize ArcGIS Server content with the metadata catalog.
*/
public class AGSSynchronizer implements Runnable, IScheduledTask {
/** class variables ========================================================= */
/** Logger */
private static Logger LOGGER = Logger.getLogger(AGSSynchronizer.class.getName());
/** instance variables ====================================================== */
private StringAttributeMap parameters;
/** constructors =========================================================== */
/** Default constructor. */
public AGSSynchronizer() {}
/** properties ============================================================= */
/**
* Sets the configuration paramaters for the task.
* @param parameters the configuration paramaters
*/
public void setParameters(StringAttributeMap parameters) {
this.parameters = parameters;
}
/** methods ================================================================= */
/**
* Run the synchronization process.
*/
public void run() {
LOGGER.info("AGSSynchronizer run started...");
RequestContext rContext = null;
Lock backgroundLock = null;
long tStartMillis = System.currentTimeMillis();
try {
// initialize
String restUrl = "";
String soapUrl = "";
PublicationRecord template = new PublicationRecord();
if (this.parameters != null) {
restUrl = Val.chkStr(this.parameters.getValue("restUrl"));
soapUrl = Val.chkStr(this.parameters.getValue("soapUrl"));
template.setAutoApprove(
Val.chkStr(this.parameters.getValue("autoApprove")).equalsIgnoreCase("true"));
template.setUpdateOnlyIfXmlHasChanged(
Val.chkStr(this.parameters.getValue("updateOnlyIfXmlHasChanged")).equalsIgnoreCase("true"));
}
if (restUrl.length() == 0) {
LOGGER.log(Level.SEVERE,"AGSSynchronizer run aborted: the restUrl parameter was empty.");
return;
}
if (soapUrl.length() == 0) {
LOGGER.log(Level.SEVERE,"AGSSynchronizer run aborted: the soapUrl parameter was empty.");
return;
}
// obtain the background thread lock,
// sleep for 10 minutes if busy then try again
rContext = RequestContext.extract(null);
LuceneIndexAdapter adapter = new LuceneIndexAdapter(rContext);
adapter.touch(); // ensures that a proper directory structure exists
try {
backgroundLock = adapter.obtainBackgroundLock();
} catch (LockObtainFailedException lofe) {
if (Thread.currentThread().isInterrupted()) return;
try {
Thread.sleep(10 * 1000);
} catch (InterruptedException e) {
throw new IOException(e.toString());
}
if (Thread.currentThread().isInterrupted()) return;
backgroundLock = adapter.obtainBackgroundLock();
}
if (Thread.currentThread().isInterrupted()) return;
// process services on the ArcGIS server
StringBuilder sbSummary = new StringBuilder();
Publisher publisher = Publisher.makeSystemAdministrator(rContext);
HttpClientRequest httpClient = HttpClientRequest.newRequest();
ProcessingContext pContext = new ProcessingContext(rContext,publisher,httpClient,template,false);
AGSProcessor ags = new AGSProcessor(pContext);
ags.getTarget().setRestUrl(restUrl);
ags.getTarget().setSoapUrl(soapUrl);
ags.getTarget().setTargetUrl(restUrl);
ags.getTarget().setTargetType(AGSTarget.TargetType.ROOT);
if (!Thread.currentThread().isInterrupted()) {
ags.process();
}
sbSummary.append("\n numCreated=").append(pContext.getNumberCreated());
sbSummary.append(", numReplaced=").append(pContext.getNumberReplaced());
sbSummary.append(", numUnchanged=").append(pContext.getNumberUnchanged());
sbSummary.append(", numDeleted=").append(pContext.getNumberDeleted());
sbSummary.append(", numFailed=").append(pContext.getNumberFailed());
// log a summary message
double dSec = (System.currentTimeMillis() - tStartMillis) / 1000.0;
StringBuilder msg = new StringBuilder();
msg.append("AGSSynchronizer run completed.");
msg.append("\n restUrl=").append(restUrl);
msg.append("\n soapUrl=").append(soapUrl);
msg.append(sbSummary.toString());
msg.append("\n wasInterrupted=").append(Thread.currentThread().isInterrupted());
msg.append(", runtime: ");
msg.append(Math.round(dSec / 60.0 * 100.0) / 100.0).append(" minutes");
if (dSec <= 600) {
msg.append(", ").append(Math.round(dSec * 100.0) / 100.0).append(" seconds");
}
LOGGER.info(msg.toString());
} catch (ImsServiceException e) {
LOGGER.log(Level.SEVERE,"Deletion error.",e);
} catch (CatalogIndexException e) {
LOGGER.log(Level.SEVERE,"Catalog index error.",e);
} catch (SQLException e) {
LOGGER.log(Level.SEVERE,"Database error.",e);
} catch (Exception e) {
LOGGER.log(Level.SEVERE,"Unknown error.",e);
} finally {
if (backgroundLock != null) {
try {
backgroundLock.release();
} catch (Throwable t) {
LOGGER.log(Level.WARNING,"Error releasing lock.",t);
}
}
if (rContext != null) {
rContext.onExecutionPhaseCompleted();
}
if (Thread.currentThread().isInterrupted()) {
LOGGER.info("AGSSynchronizer run was interrupted.");
}
}
}
}