/*
* Copyright 2013 JBoss 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 org.artificer.ui.server.services;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import javax.enterprise.context.ApplicationScoped;
import org.apache.commons.lang3.StringUtils;
import org.artificer.client.ArtificerClientException;
import org.artificer.client.ArtificerClientQuery;
import org.artificer.client.query.QueryResultSet;
import org.artificer.common.ArtifactType;
import org.artificer.common.error.ArtificerServerException;
import org.artificer.common.query.ArtifactSummary;
import org.artificer.ui.client.shared.beans.ArtifactFilterBean;
import org.artificer.ui.client.shared.beans.ArtifactResultSetBean;
import org.artificer.ui.client.shared.beans.ArtifactSearchBean;
import org.artificer.ui.client.shared.beans.ArtifactSummaryBean;
import org.artificer.ui.client.shared.beans.ArtifactTypeBean;
import org.artificer.ui.client.shared.exceptions.ArtificerUiException;
import org.artificer.ui.client.shared.services.IArtifactSearchService;
import org.artificer.ui.server.api.ArtificerApiClientAccessor;
/**
* Concrete implementation of the artifact search service.
*
* @author eric.wittmann@redhat.com
*/
@ApplicationScoped
public class ArtifactSearchService implements IArtifactSearchService {
/**
* Constructor.
*/
public ArtifactSearchService() {
}
@Override
public ArtifactResultSetBean search(ArtifactSearchBean searchBean) throws ArtificerUiException {
int pageSize = 20;
try {
ArtifactResultSetBean rval = new ArtifactResultSetBean();
int req_startIndex = (searchBean.getPage() - 1) * pageSize;
ArtificerClientQuery query = ArtificerApiClientAccessor.getClient().buildQuery(searchBean.getQueryText());
ArtificerClientQuery sq = query.startIndex(req_startIndex).orderBy(searchBean.getSortColumnId());
if (searchBean.isSortAscending()) {
sq.ascending();
} else {
sq.descending();
}
QueryResultSet resultSet = sq.count(pageSize + 1).query();
ArrayList<ArtifactSummaryBean> artifacts = new ArrayList<ArtifactSummaryBean>();
for (ArtifactSummary artifactSummary : resultSet) {
ArtifactSummaryBean bean = new ArtifactSummaryBean();
ArtifactType artifactType = artifactSummary.getArtifactType();
bean.setModel(artifactType.getArtifactType().getModel());
bean.setType(artifactType.getType());
bean.setRawType(artifactType.getArtifactType().getType());
bean.setUuid(artifactSummary.getUuid());
bean.setName(artifactSummary.getName());
bean.setDescription(artifactSummary.getDescription());
bean.setCreatedBy(artifactSummary.getCreatedBy());
bean.setCreatedOn(artifactSummary.getCreatedTimestamp().getTime());
bean.setUpdatedOn(artifactSummary.getLastModifiedTimestamp().getTime());
bean.setDerived(artifactType.isDerived());
artifacts.add(bean);
}
boolean hasMorePages = false;
if (artifacts.size() > pageSize) {
artifacts.remove(artifacts.get(artifacts.size()-1));
hasMorePages = true;
}
// Does the server support opensearch style attributes? If so,
// use that information. Else figure it out from the request params.
if (resultSet.getTotalResults() != -1) {
rval.setItemsPerPage(pageSize);
rval.setStartIndex(resultSet.getStartIndex());
rval.setTotalResults(resultSet.getTotalResults());
} else {
rval.setItemsPerPage(pageSize);
rval.setTotalResults(hasMorePages ? pageSize + 1 : artifacts.size());
rval.setStartIndex(req_startIndex);
}
rval.setArtifacts(artifacts);
return rval;
} catch (ArtificerClientException e) {
throw new ArtificerUiException(e.getMessage());
} catch (ArtificerServerException e) {
throw new ArtificerUiException(e.getMessage());
}
}
@Override
public String query(ArtifactFilterBean filters) throws ArtificerUiException {
StringBuilder queryBuilder = new StringBuilder();
// Initial query
queryBuilder.append("/s-ramp");
// Artifact type
if (filters.getArtifactType() != null && filters.getArtifactType().trim().length() > 0) {
ArtifactType type = ArtifactType.valueOf(filters.getArtifactType());
queryBuilder.append("/").append(type.getModel()).append("/").append(type.getType());
}
List<String> criteria = new ArrayList<String>();
if (filters.getKeywords() != null && filters.getKeywords().trim().length() > 0) {
criteria.add("xp2:matches(., '" + filters.getKeywords() + "')");
}
if (filters.getUuid() != null && filters.getUuid().trim().length() > 0) {
criteria.add("@uuid = '" + filters.getUuid() + "'");
}
if (filters.getName() != null && filters.getName().trim().length() > 0) {
criteria.add("@name = '" + filters.getName() + "'");
}
// Created on
if (filters.getDateCreatedFrom() != null) {
Calendar cal = Calendar.getInstance();
cal.setTime(filters.getDateCreatedFrom());
zeroOutTime(cal);
criteria.add("@createdTimestamp >= " + cal.getTimeInMillis());
}
if (filters.getDateCreatedTo() != null) {
Calendar cal = Calendar.getInstance();
cal.setTime(filters.getDateCreatedTo());
zeroOutTime(cal);
cal.add(Calendar.DAY_OF_YEAR, 1);
criteria.add("@createdTimestamp < " + cal.getTimeInMillis());
}
// Last Modified on
if (filters.getDateModifiedFrom() != null) {
Calendar cal = Calendar.getInstance();
cal.setTime(filters.getDateModifiedFrom());
zeroOutTime(cal);
criteria.add("@lastModifiedTimestamp >= " + cal.getTimeInMillis());
}
if (filters.getDateModifiedTo() != null) {
Calendar cal = Calendar.getInstance();
cal.setTime(filters.getDateModifiedTo());
zeroOutTime(cal);
cal.add(Calendar.DAY_OF_YEAR, 1);
criteria.add("@lastModifiedTimestamp < " + cal.getTimeInMillis());
}
// Created By
if (filters.getCreatedBy() != null && filters.getCreatedBy().trim().length() > 0) {
criteria.add("@createdBy = '" + filters.getCreatedBy() + "'");
}
// Last Modified By
if (filters.getLastModifiedBy() != null && filters.getLastModifiedBy().trim().length() > 0) {
criteria.add("@lastModifiedBy = '" + filters.getLastModifiedBy() + "'");
}
// Origin
switch (filters.getOrigin()) {
case PRIMARY_ORIGINAL:
criteria.add("@derived = 'false' and @expandedFromArchive = 'false'");
break;
case PRIMARY_EXPANDED:
criteria.add("@derived = 'false' and @expandedFromArchive = 'true'");
break;
case DERIVED:
criteria.add("@derived = 'true'");
break;
}
// Classifiers
if (hasClassifiers(filters)) {
Set<String> ontologyBases = filters.getClassifiers().keySet();
StringBuilder classifierCriteria = new StringBuilder();
classifierCriteria.append("s-ramp:classifiedByAllOf(.");
for (String base : ontologyBases) {
Set<String> ids = filters.getClassifiers().get(base);
for (String id : ids) {
String classifierUri = base + "#" + id;
classifierCriteria.append(",'" + classifierUri + "'");
}
}
classifierCriteria.append(")");
criteria.add(classifierCriteria.toString());
}
// Custom properties
if (!filters.getCustomProperties().isEmpty()) {
for (Entry<String, String> entry : filters.getCustomProperties().entrySet()) {
String propName = entry.getKey();
String propVal = entry.getValue();
// Note: this looks dangerous (injection) but know that
// S-RAMP queries are read-only. Also, the UI allows
// the user to input any query they want anyway (via the
// query text box)...
if (propVal == null || propVal.trim().length() == 0) {
criteria.add("@" + propName);
} else {
criteria.add("@" + propName + " = '" + propVal + "'");
}
}
}
// Now create the query predicate from the generated criteria
if (criteria.size() > 0) {
queryBuilder.append("[");
queryBuilder.append(StringUtils.join(criteria, " and "));
queryBuilder.append("]");
}
return queryBuilder.toString();
}
/**
* Returns true if the filters has *at least* one classifier configured.
* @param filters
*/
protected boolean hasClassifiers(ArtifactFilterBean filters) {
Map<String, Set<String>> classifiers = filters.getClassifiers();
if (classifiers == null)
return false;
for (String key : classifiers.keySet()) {
Set<String> oclasses = classifiers.get(key);
if (oclasses != null && !oclasses.isEmpty()) {
return true;
}
}
return false;
}
/**
* Set the time components of the given {@link Calendar} to 0's.
* @param cal
*/
protected void zeroOutTime(Calendar cal) {
cal.set(Calendar.HOUR, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
}
@Override
public List<ArtifactTypeBean> types() throws ArtificerUiException {
List<String> types = null;
try {
types = ArtificerApiClientAccessor.getClient().getTypes();
} catch (ArtificerClientException e) {
throw new ArtificerUiException(e.getMessage());
} catch (ArtificerServerException e) {
throw new ArtificerUiException(e.getMessage());
}
List<ArtifactTypeBean> returnTypes = new ArrayList<ArtifactTypeBean>();
if (types != null && !types.isEmpty()) {
for (String type : types) {
returnTypes.add(new ArtifactTypeBean(type));
}
}
return returnTypes;
}
}