/* (c) 2014 - 2016 Open Source Geospatial Foundation - all rights reserved
* This code is licensed under the GPL 2.0 license, available at the root
* application directory.
*/
package org.geoserver.wps.web;
import static org.geoserver.catalog.Predicates.acceptAll;
import static org.geoserver.catalog.Predicates.or;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.logging.Logger;
import org.apache.wicket.extensions.markup.html.repeater.util.SortParam;
import org.geoserver.catalog.Predicates;
import org.geoserver.web.GeoServerApplication;
import org.geoserver.web.wicket.GeoServerDataProvider;
import org.geoserver.web.wicket.GeoServerDataProvider.Property;
import org.geoserver.wps.ProcessStatusStore;
import org.geoserver.wps.executor.ExecutionStatus;
import org.geoserver.wps.executor.ProcessStatusTracker;
import org.geotools.data.Query;
import org.geotools.factory.CommonFactoryFinder;
import org.geotools.filter.SortByImpl;
import org.geotools.util.logging.Logging;
import org.opengis.filter.Filter;
import org.opengis.filter.FilterFactory2;
import org.opengis.filter.expression.ExpressionVisitor;
import org.opengis.filter.expression.PropertyName;
import org.opengis.filter.sort.SortBy;
import org.opengis.filter.sort.SortOrder;
import org.xml.sax.helpers.NamespaceSupport;
/**
* Provides a filtered, sorted view over the running/recently completed processes
*
* @author Andrea Aime - GeoSolutions
*/
@SuppressWarnings("serial")
public class ProcessStatusProvider extends GeoServerDataProvider<ExecutionStatus> {
static private Logger LOGGER = Logging.getLogger(ProcessStatusProvider.class);
static private final FilterFactory2 FF = CommonFactoryFinder.getFilterFactory2();
static final Property<ExecutionStatus> TYPE = new AbstractProperty<ExecutionStatus>("type") {
@Override
public Object getPropertyValue(ExecutionStatus item) {
// we might want to have a "C" state for the chained processes
return item.isAsynchronous() ? "A" : "S";
}
@Override
public boolean isSearchable() {
// when really it isn't sortable or searchable
return false;
}
};
static final Property<ExecutionStatus> NODE = new BeanProperty<ExecutionStatus>("node",
"nodeId");
static final Property<ExecutionStatus> USER = new BeanProperty<ExecutionStatus>("user",
"userName");
static final Property<ExecutionStatus> PROCESS = new BeanProperty<ExecutionStatus>(
"processName", "processName");
static final Property<ExecutionStatus> CREATED = new BeanProperty<ExecutionStatus>(
"creationTime", "creationTime");
static final Property<ExecutionStatus> PHASE = new BeanProperty<ExecutionStatus>("phase",
"phase");
static final Property<ExecutionStatus> PROGRESS = new BeanProperty<ExecutionStatus>("progress",
"progress");
static final Property<ExecutionStatus> TASK = new BeanProperty<ExecutionStatus>("task", "task");
static final List<Property<ExecutionStatus>> PROPERTIES = Arrays.asList(TYPE, NODE, USER,
PROCESS, CREATED, PHASE, PROGRESS, TASK);
private long first;
private long count;
@Override
protected List<ExecutionStatus> getItems() {
ProcessStatusTracker tracker = GeoServerApplication.get().getBeanOfType(
ProcessStatusTracker.class);
return tracker.getStore().list(Query.ALL);
}
@Override
protected List<Property<ExecutionStatus>> getProperties() {
return PROPERTIES;
}
@Override
protected Filter getFilter() {
final String[] keywords = getKeywords();
Filter filter = acceptAll();
if (null != keywords) {
for (String keyword : keywords) {
Filter propContains = getFullSearch(keywords);
// chain the filters together
if (Filter.INCLUDE == filter) {
filter = propContains;
} else {
filter = or(filter, propContains);
}
}
}
return filter;
}
private Filter getFullSearch(String[] keywords) {
ProcessStatusTracker tracker = GeoServerApplication.get().getBeanOfType(
ProcessStatusTracker.class);
ProcessStatusStore store = tracker.getStore();
Filter ret = Filter.INCLUDE;
if(store.supportsPredicate()) {
for (String keyword : keywords) {
Filter propContains = Predicates.fullTextSearch(keyword);
// chain the filters together
if (Filter.INCLUDE == ret) {
ret = propContains;
} else {
ret = or(ret, propContains);
}
}
}else {
if(keywords.length>0) {
List<Filter> likes = new ArrayList<Filter>();
for(String word:keywords) {
for(Property<?> prop:getProperties()) {
if(prop.isSearchable()) {
if(prop.equals(NODE)||
prop.equals(PHASE)||
prop.equals(TASK)||
prop.equals(USER)||
prop.equals(PROCESS)) {
likes.add(FF.like(FF.property(prop.getName()), "*"+word+"*"));
}
//TODO: support temporal properties if I can work out what searching means
}
}
}
ret = FF.or(likes);
}
}
return ret;
}
@Override
protected List<ExecutionStatus> getFilteredItems() {
ProcessStatusTracker tracker = GeoServerApplication.get().getBeanOfType(
ProcessStatusTracker.class);
ProcessStatusStore store = tracker.getStore();
Query query = new Query("status", getFilter());
if(count>0) {
query.setStartIndex((int) first);
query.setMaxFeatures((int) count);
}
SortParam sort = getSort();
if(sort!=null) {
SortByImpl[] sortBys = new SortByImpl[1];
final Property<?> property = getProperty(sort);
if(property.isSearchable()) {//we really need another flag
FF.sort(property.getName(), SortOrder.ASCENDING);
if(!sort.isAscending()) {
sortBys[0].setSortOrder(SortOrder.DESCENDING);
}
query.setSortBy(sortBys);
}
}
LOGGER.fine("built query "+query+" to filter statuses");
return store.list(query);
}
@Override
public Iterator<ExecutionStatus> iterator(long first, long count) {
this.first = first;
this.count = count;
Iterator<ExecutionStatus> it = super.iterator(first, count);
this.first = 0;
this.count = -1;
return it;
}
}