Working on Task #6262

git-svn-id: http://svn.research-infrastructures.eu/public/d4science/gcube/trunk/data-transfer/uri-resolver@141270 82a268e6-3cf1-43bd-a215-b396298e98cf
This commit is contained in:
Francesco Mangiacrapa 2016-12-20 11:11:35 +00:00
parent 9b5fab4278
commit 248dd59566
7 changed files with 383 additions and 56 deletions

View File

@ -4,9 +4,6 @@
<wb-resource deploy-path="/" source-path="/src/main/webapp" tag="defaultRootSource"/>
<wb-resource deploy-path="/WEB-INF/classes" source-path="/src/main/java"/>
<wb-resource deploy-path="/WEB-INF/classes" source-path="/src/main/resources"/>
<dependent-module archiveName="gcube-url-shortener-1.1.0-SNAPSHOT.jar" deploy-path="/WEB-INF/lib" handle="module:/resource/gcube-url-shortener-BRANCH-1.1.0-SNAPSHOT/gcube-url-shortener-BRANCH-1.1.0-SNAPSHOT">
<dependency-type>uses</dependency-type>
</dependent-module>
<property name="context-root" value="uri-resolver"/>
<property name="java-output-path" value="/uri-resolver/target/classes"/>
</wb-module>

View File

@ -13,6 +13,8 @@ import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import org.gcube.datatransfer.resolver.catalogue.CatalogueRequestParameter;
import org.gcube.datatransfer.resolver.catalogue.UrlEncoderUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -40,7 +42,8 @@ public class UriResolverRewriteFilter implements Filter{
public static final String PARAMETER_SMP_ID = "smp-id";
public static final String SERVLET_STORAGE_ID = "id";
public static final String PARAMETER_CATALOGUE_LINK = "cl";
public static final String PARAMETER_ENC_CATALOGUE_LINK = "cl";
public static final String PARAMETER_DIRECT_CATALOGUE_LINK = "dl";
public static final String SERVLET_CATALOGUE = "/catalogue";
protected static final Logger logger = LoggerFactory.getLogger(UriResolverRewriteFilter.class);
@ -84,16 +87,37 @@ public class UriResolverRewriteFilter implements Filter{
}else if(multiReadRequest.getServletPath().startsWith(SERVLET_CATALOGUE) || multiReadRequest.getServletPath().startsWith(SERVLET_URI_RESOLVER+SERVLET_CATALOGUE)){
logger.debug("is a catalogue request");
int lastSlash = requestURI.lastIndexOf(PATH_SEPARATOR);
String toCatalogueLink = requestURI.substring(lastSlash + 1, requestURI.length());
//int startIndex = requestURI.indexOf(SERVLET_CATALOGUE))+SERVLET_CATALOGUE.length();
//String vreName = requestURI.substring(beginIndex)
HttpServletRequest request = (HttpServletRequest) req;
String newURI = SERVLET_CATALOGUE;
logger.debug("method is: "+request.getMethod());
if(request.getMethod().compareTo("GET")==0)
newURI+= "?" + PARAMETER_CATALOGUE_LINK + "=" + toCatalogueLink;
String[] pathSplit = multiReadRequest.getServletPath().split(PATH_SEPARATOR);
String newURI = SERVLET_CATALOGUE;
if(pathSplit.length==5){
logger.info("Resolving a clear URL to catalogue...");
logger.debug("found VRE name: "+pathSplit[2]);
String gcubeScope = CatalogueRequestParameter.GCUBE_SCOPE.getKey() +"="+"/gcube/devNext/NextNext";
String eC = CatalogueRequestParameter.ENTITY_CONTEXT.getKey() +"="+pathSplit[3];
logger.debug("found context name: "+eC);
String eN = CatalogueRequestParameter.ENTITY_NAME.getKey() +"="+pathSplit[4];
logger.debug("found entity name: "+eN);
String encodedQuery = UrlEncoderUtil.encodeQuery(gcubeScope,eC,eN);
if(request.getMethod().compareTo("GET")==0)
newURI+= "?" + PARAMETER_DIRECT_CATALOGUE_LINK + "=" + encodedQuery;
}else{
logger.info("Resolving an encrypted URL to catalogue...");
int lastSlash = requestURI.lastIndexOf(PATH_SEPARATOR);
String toCatalogueLink = requestURI.substring(lastSlash + 1, requestURI.length());
if(request.getMethod().compareTo("GET")==0)
newURI+= "?" + PARAMETER_ENC_CATALOGUE_LINK + "=" + toCatalogueLink;
}
logger.debug("forward to: " + newURI);
multiReadRequest.getRequestDispatcher(newURI).forward(multiReadRequest, response);
//chain.doFilter(multiReadRequest, response);
}else{
//IS WORKSPACE REQUEST?
@ -141,4 +165,20 @@ public class UriResolverRewriteFilter implements Filter{
logger.trace("run init");
this.config = config;
}
public static void main(String[] args) {
String split = "/catalogue/NextNext/dataset/sarda-sarda";
String[] array = split.split("/");
System.out.println(array.length);
for (int i = 0; i < array.length; i++) {
System.out.println(i+" "+array[i]);
}
System.out.println(array[2]);
}
}

View File

@ -0,0 +1,226 @@
package org.gcube.datatransfer.resolver.catalogue;
import static org.gcube.resources.discovery.icclient.ICFactory.client;
import java.io.StringReader;
import java.util.List;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.gcube.common.resources.gcore.utils.XPathHelper;
import org.gcube.common.scope.api.ScopeProvider;
import org.gcube.datatransfer.resolver.applicationprofile.ApplicationProfile;
import org.gcube.datatransfer.resolver.applicationprofile.ApplicationProfileNotFoundException;
import org.gcube.datatransfer.resolver.applicationprofile.GcubeQuery;
import org.gcube.datatransfer.resolver.applicationprofile.ScopeUtil;
import org.gcube.resources.discovery.client.api.DiscoveryClient;
import org.gcube.resources.discovery.client.queries.api.Query;
import org.gcube.resources.discovery.client.queries.impl.QueryBox;
import org.w3c.dom.Node;
import org.xml.sax.InputSource;
/**
* The Class ApplicationProfileReader.
*
* @author Francesco Mangiacrapa francesco.mangiacrapa@isti.cnr.it
* Sep 6, 2016
*/
public class ApplicationProfileReaderForCatalogueResolver {
protected static final String RESOURCE_PROFILE_BODY_END_POINT_VRE_TEXT = "/Resource/Profile/Body/EndPoint/URL/text()";
protected static final String RESOURCE_PROFILE_BODY_END_POINT_SCOPE_TEXT = "/Resource/Profile/Body/EndPoint/Scope/text()";
protected static final String RESOURCE_PROFILE_BODY_TEXT = "/Resource/Profile/Body/text()";
protected static final String RESOURCE_PROFILE_BODY_THUMBNAIL_URL_TEXT = "/Resource/Profile/Body/ThumbnailURL/text()";
protected static final String RESOURCE_PROFILE_BODY_APP_ID_TEXT = "/Resource/Profile/Body/AppId/text()";
protected static final String RESOURCE_PROFILE_DESCRIPTION_TEXT = "/Resource/Profile/Description/text()";
protected static final String RESOURCE_PROFILE_NAME_TEXT = "/Resource/Profile/Name/text()";
protected static final String SECONDARY_TYPE = "ApplicationProfile";
protected static final String RESOURCE_NAME = "Catalogue-Resolver";
private Logger logger = Logger.getLogger(ApplicationProfileReaderForCatalogueResolver.class);
private String secondaryType;
private String resourceName;
private String scope;
private ApplicationProfile applicationProfile;
private boolean useRootScope = false;
/**
* Instantiates a new application profile reader for catalogue resolver.
*
* @param scope the scope
* @param portletClassName the portlet class name
* @param useRootScope the use root scope
*/
public ApplicationProfileReaderForCatalogueResolver(String scope, boolean useRootScope) {
this.scope = scope;
this.secondaryType = SECONDARY_TYPE;
this.resourceName = RESOURCE_NAME;
this.useRootScope = useRootScope;
this.applicationProfile = readProfileFromInfrastrucure();
}
/**
* Gets the application profile.
*
* @return the application profile
*/
public ApplicationProfile getApplicationProfile() {
return applicationProfile;
}
/**
* this method looks up the applicationProfile profile among the ones available in the infrastructure.
*
* @return the applicationProfile profile
*/
private ApplicationProfile readProfileFromInfrastrucure() {
ApplicationProfile appProf = new ApplicationProfile();
String queryString = GcubeQuery.getGcubeGenericQueryString(secondaryType, resourceName);
String originalScope = null;
try {
originalScope = ScopeProvider.instance.get();
String discoveryScope = useRootScope?ScopeUtil.getInfrastructureNameFromScope(scope):scope;
ScopeProvider.instance.set(discoveryScope);
logger.info("Trying to fetch ApplicationProfile in the infra scope: "+discoveryScope+", SecondaryType: " + secondaryType + ", AppId: " + resourceName);
Query q = new QueryBox(queryString);
DiscoveryClient<String> client = client();
List<String> appProfile = client.submit(q);
if (appProfile == null || appProfile.size() == 0)
throw new ApplicationProfileNotFoundException("ApplicationProfile with SecondaryType: " + secondaryType + ", AppId: " + resourceName +" is not registered in the infra scope: "+discoveryScope);
else {
String elem = appProfile.get(0);
DocumentBuilder docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
Node node = docBuilder.parse(new InputSource(new StringReader(elem))).getDocumentElement();
XPathHelper helper = new XPathHelper(node);
List<String> currValue = null;
currValue = helper.evaluate(RESOURCE_PROFILE_NAME_TEXT);
if (currValue != null && currValue.size() > 0) {
appProf.setName(currValue.get(0));
}
else throw new ApplicationProfileNotFoundException("Your applicationProfile NAME was not found in the profile");
currValue = helper.evaluate(RESOURCE_PROFILE_DESCRIPTION_TEXT);
if (currValue != null && currValue.size() > 0) {
appProf.setDescription(currValue.get(0));
}
else logger.warn("No Description exists for " + appProf.getName());
currValue = helper.evaluate(RESOURCE_PROFILE_BODY_APP_ID_TEXT);
if (currValue != null && currValue.size() > 0) {
appProf.setKey(currValue.get(0));
}
else throw new ApplicationProfileNotFoundException("Your applicationProfile ID n was not found in the profile, consider adding <AppId> element in <Body>");
currValue = helper.evaluate(RESOURCE_PROFILE_BODY_THUMBNAIL_URL_TEXT);
if (currValue != null && currValue.size() > 0) {
appProf.setImageUrl(currValue.get(0));
}
else{
logger.warn("Null or empty <ThumbnailURL> element in <Body>" + appProf.getName());
}
currValue = helper.evaluate("/Resource/Profile/Body/EndPoint[Scope='"+scope.toString()+"']/Scope/text()");
if (currValue != null && currValue.size() > 0) {
List<String> scopes = currValue;
String currentScope = scopes.get(0);
int slashCount = StringUtils.countMatches(currentScope, "/");
if(slashCount < 3){//CASE not VRE - set session scope
logger.info("Scope "+ scope.toString() + " is not a VRE");
List<String> listSessionScope = helper.evaluate("/Resource/Profile/Body/EndPoint[Scope='"+scope.toString()+"']/Sessionscope/text()"); //get session scope of i+1-mo scope
if(listSessionScope!=null && listSessionScope.size()>0){ //If sessions scope exists
logger.trace("setting session scope "+ listSessionScope.get(0));
appProf.setScope(listSessionScope.get(0));
}
else{
logger.trace("session scope not exists setting scope "+ scope.toString());
appProf.setScope(scope.toString());
}
}
else{ //CASE IS A VRE
logger.info("Scope "+ scope.toString() + " is a VRE");
appProf.setScope(scope.toString());
}
//RETRIEVE URL
currValue = helper.evaluate("/Resource/Profile/Body/EndPoint[Scope='"+scope.toString()+"']/URL/text()");
if (currValue != null && currValue.size() > 0) {
String url = currValue.get(0);
// System.out.println("URL "+url);
if(url!=null)
appProf.setUrl(url);
else
throw new ApplicationProfileNotFoundException("Your applicationProfile URL was not found in the profile for Scope: " + scope.toString());
}
else throw new ApplicationProfileNotFoundException("Your applicationProfile URL was not found in the profile for Scope: " + scope.toString());
}
else throw new ApplicationProfileNotFoundException("Your applicationProfile with scope "+scope.toString()+" was not found in the profile, consider adding <EndPoint><Scope> element in <Body>");
return appProf;
}
} catch (Exception e) {
logger.error("Error while trying to fetch applicationProfile profile from the infrastructure", e);
return null;
}finally{
if(originalScope!=null && !originalScope.isEmpty()){
ScopeProvider.instance.set(originalScope);
logger.info("scope provider setted to orginal scope: "+originalScope);
}else{
ScopeProvider.instance.reset();
logger.info("scope provider reset");
}
}
}
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("ApplicationProfileReader [secondaryType=");
builder.append(secondaryType);
builder.append(", appId=");
builder.append(resourceName);
builder.append(", scope=");
builder.append(scope);
builder.append(", applicationProfile=");
builder.append(applicationProfile);
builder.append("]");
return builder.toString();
}
/*
public static void main(String[] args) {
String portletClassName = "org.gcube.portlets.user.gisviewerapp.server.GisViewerAppServiceImpl";
String scope ="/gcube";
String secondaryType = "ApplicationProfile";
ApplicationProfileReader reader = new ApplicationProfileReader(scope, secondaryType, portletClassName);
System.out.println(reader);
}*/
}

View File

@ -37,7 +37,21 @@ public class CatalogueEntityRequest {
}
/**
* Removes the parameter to request.
*
* @param key the key
*/
public void removeParameterToRequest(String key) {
this.parameters.remove(key);
}
/**
* Gets the parameters.
*
* @return the parameters
*/
public Map<String, String> getParameters() {

View File

@ -15,6 +15,7 @@ public enum CatalogueRequestParameter {
GCUBE_SCOPE("gcube_scope",true),
ENTITY_CONTEXT("entity_context",true),
ENTITY_NAME("entity_name",true),
CLEAR_URL("clear_url",false),
QUERY_STRING("query_string",false);
private String key;

View File

@ -38,7 +38,9 @@ public class CatalogueResolver extends HttpServlet{
private static final String TEXT_PALIN_CHARSET_UTF_8 = "text/plain;charset=UTF-8";
public static final String UTF_8 = "UTF-8";
public static final String CATALOGUE_LINK_PARAM = UriResolverRewriteFilter.PARAMETER_CATALOGUE_LINK;
public static final String ENC_CATALOGUE_LINK_PARAM = UriResolverRewriteFilter.PARAMETER_ENC_CATALOGUE_LINK;
public static final String DIRECT_CATALOGUE_LINK_PARAM = UriResolverRewriteFilter.PARAMETER_DIRECT_CATALOGUE_LINK;
private static final String PATH_SEPARATOR = "/";
@ -89,46 +91,63 @@ public class CatalogueResolver extends HttpServlet{
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//ScopeProvider.instance.set(scope);
String catalogueQueryLink = req.getParameter(CATALOGUE_LINK_PARAM);
logger.info("Trying to decode encoded catalogue query link: "+catalogueQueryLink);
if(catalogueQueryLink==null || catalogueQueryLink.isEmpty()){
logger.error("Data Catalogue Link is malformed, set "+CATALOGUE_LINK_PARAM+" parameter");
sendError(resp, HttpStatus.SC_BAD_REQUEST, "Data Catalogue Link is malformed, set "+CATALOGUE_LINK_PARAM+" parameter");
return;
}
String clearCatalogueQueryLink = req.getParameter(DIRECT_CATALOGUE_LINK_PARAM);
String encryptedCatalogueQueryLink = req.getParameter(ENC_CATALOGUE_LINK_PARAM);
String base64DecodedId = "";
String decryptedDCId = "";
try {
boolean foundLink = false;
if(clearCatalogueQueryLink==null || clearCatalogueQueryLink.isEmpty()){
logger.info("Link is not a direct link to catalogue");
}else
foundLink = true;
base64DecodedId = base64DecodeString(catalogueQueryLink);
logger.info("Base 64 decoded Data Catalogue Link: "+base64DecodedId +", now decrypting...");
String queryStringToCat = "";
if(foundLink){
logger.info("Trying to resolve clear link to catalogue using query link: "+clearCatalogueQueryLink);
queryStringToCat = clearCatalogueQueryLink;
}else{
logger.info("Trying to resolve encripted link to catalogue using query link: "+encryptedCatalogueQueryLink);
if(encryptedCatalogueQueryLink==null || encryptedCatalogueQueryLink.isEmpty()){
logger.error("Data Catalogue Link is malformed, set "+ENC_CATALOGUE_LINK_PARAM+" parameter");
sendError(resp, HttpStatus.SC_BAD_REQUEST, "Data Catalogue Link is malformed, either you must set "+ENC_CATALOGUE_LINK_PARAM+" parameter or "+DIRECT_CATALOGUE_LINK_PARAM+ " parameter");
return;
}
String base64DecodedId = "";
try {
if(scopeToEncDecr==null)
initScopeFromEnv();
base64DecodedId = base64DecodeString(encryptedCatalogueQueryLink);
logger.info("Base 64 decoded Data Catalogue Link: "+base64DecodedId +", now decrypting...");
ScopeProvider.instance.set(scopeToEncDecr);
decryptedDCId = StringEncrypter.getEncrypter().decrypt(base64DecodedId);
logger.info("Decrypted Data Catalogue Link: "+decryptedDCId);
if(scopeToEncDecr==null)
initScopeFromEnv();
}catch (Exception e) {
logger.error("An error occurred during decrypting data catalogue link: "+base64DecodedId+", using the scope: "+scopeToEncDecr, e);
sendError(resp, HttpStatus.SC_INTERNAL_SERVER_ERROR, "The system cannot decrypt the Catalogue Link");
return;
ScopeProvider.instance.set(scopeToEncDecr);
queryStringToCat = StringEncrypter.getEncrypter().decrypt(base64DecodedId);
logger.info("Decrypted Data Catalogue Link: "+queryStringToCat);
}catch (Exception e) {
logger.error("An error occurred during decrypting data catalogue link: "+base64DecodedId+", using the scope: "+scopeToEncDecr, e);
sendError(resp, HttpStatus.SC_INTERNAL_SERVER_ERROR, "The system cannot decrypt the Catalogue Link");
return;
}
}
CatalogueEntityRequest cer = new CatalogueEntityRequest();
for (CatalogueRequestParameter parameter : CatalogueRequestParameter.values()) {
String value = getValueOfParameter(parameter.getKey(), decryptedDCId);
String value = getValueOfParameter(parameter.getKey(), queryStringToCat);
cer.addParameterToRequest(parameter.getKey(), value);
}
if(cer.getValueOfParameter(CatalogueRequestParameter.ENTITY_CONTEXT.getKey()).compareToIgnoreCase("product")==0){
logger.debug("Read "+CatalogueRequestParameter.ENTITY_CONTEXT.getKey() + " value: 'product' replacing with 'dataset'");
cer.addParameterToRequest(CatalogueRequestParameter.ENTITY_CONTEXT.getKey(), "dataset");
}
logger.debug("Read parameters: "+cer.toString());
String scope = cer.getValueOfParameter(CatalogueRequestParameter.GCUBE_SCOPE.getKey());
if(scope==null || scope.isEmpty()){
logger.error("An error occurred during resolving data catalogue link: "+base64DecodedId+", the scope to search CKan Portlet is null or empty");
logger.error("An error occurred during resolving data catalogue link: the scope to search CKan Portlet is null or empty");
sendError(resp, HttpStatus.SC_INTERNAL_SERVER_ERROR, "The system cannot resolve the Catalogue Link, the scope is null or empty");
return;
}
@ -225,28 +244,49 @@ public class CatalogueResolver extends HttpServlet{
scope+="/"+scope;
cer.addParameterToRequest(CatalogueRequestParameter.GCUBE_SCOPE.getKey(), scope);
}
originalScope = ScopeProvider.instance.get();
logger.info("Using scope "+scopeToEncDecr+ " from env to get encrypt key");
ScopeProvider.instance.set(scopeToEncDecr);
String buildLink = getServerURL(req);
buildLink += req.getRequestURI();
//String query = UrlEncoderUtil.encodeQuery(cer.getParameters());
String query = "";
for (String key : cer.getParameters().keySet()) {
query+=key+"="+ cer.getParameters().get(key) +"&";
String clearURL = cer.getValueOfParameter(CatalogueRequestParameter.CLEAR_URL.getKey());
boolean bClearURL = false;
try{
bClearURL = Boolean.parseBoolean(clearURL);
}catch(Exception e){
//silent
}
query = UrlEncoderUtil.removeLastChar(query);
logger.info("Builded query string: "+query);
String encriptedQuery = StringEncrypter.getEncrypter().encrypt(query);
logger.info("Encrypted query: "+encriptedQuery);
String encodedQuery = base64EncodeStringURLSafe(encriptedQuery);
logger.info("Catalogue Query Link: "+encodedQuery);
buildLink+=PATH_SEPARATOR+encodedQuery;
logger.info("Writing Catalogue Link: "+buildLink);
resp.setContentType(TEXT_PALIN_CHARSET_UTF_8);
resp.setCharacterEncoding(UTF_8);
logger.info("Clear URL is: "+bClearURL);
if(bClearURL){
String lastContext = scope.substring(scope.lastIndexOf("/")+1, scope.length());
buildLink+=PATH_SEPARATOR+lastContext+PATH_SEPARATOR+cer.getValueOfParameter(CatalogueRequestParameter.ENTITY_CONTEXT.getKey())+PATH_SEPARATOR+cer.getValueOfParameter(CatalogueRequestParameter.ENTITY_NAME.getKey());
logger.info("Writing Decoded Catalogue Link: "+buildLink);
}else{
cer.removeParameterToRequest(CatalogueRequestParameter.CLEAR_URL.getKey());
originalScope = ScopeProvider.instance.get();
logger.info("Using scope "+scopeToEncDecr+ " from env to get encrypt key");
ScopeProvider.instance.set(scopeToEncDecr);
//String query = UrlEncoderUtil.encodeQuery(cer.getParameters());
String query = "";
for (String key : cer.getParameters().keySet()) {
query+=key+"="+ cer.getParameters().get(key) +"&";
}
query = UrlEncoderUtil.removeLastChar(query);
logger.info("Builded query string: "+query);
String encriptedQuery = StringEncrypter.getEncrypter().encrypt(query);
logger.info("Encrypted query: "+encriptedQuery);
String encodedQuery = base64EncodeStringURLSafe(encriptedQuery);
logger.info("Catalogue Query Link: "+encodedQuery);
buildLink+=PATH_SEPARATOR+encodedQuery;
logger.info("Writing Encoded Catalogue Link: "+buildLink);
}
resp.getWriter().write(buildLink);
}catch(Exception e){

View File

@ -10,17 +10,19 @@ import java.util.Map;
import org.apache.log4j.Logger;
/**
* The Class UrlEncoderUtil.
*
* @author Francesco Mangiacrapa francesco.mangiacrapa@isti.cnr.it
* Dec 2, 2016
* Dec 20, 2016
*/
public class UrlEncoderUtil {
public static String charset = "UTF-8";
public static Logger logger = Logger.getLogger(UrlEncoderUtil.class);
/**
* Encode query.
*
@ -30,26 +32,33 @@ public class UrlEncoderUtil {
public static String encodeQuery(String... parameters){
String query = "";
for (String string : parameters) {
for (int i = 0; i < parameters.length-1; i++) {
try {
query+=URLEncoder.encode(string, charset)+"&";
query+=URLEncoder.encode(parameters[i], charset)+"%26";
} catch (UnsupportedEncodingException e) {
logger.error(e);
return query;
}
}
return removeLastChar(query);
try {
query+=URLEncoder.encode(parameters[parameters.length-1], charset);
} catch (UnsupportedEncodingException e) {
logger.error(e);
return query;
}
return query;
}
/**
* Encode query.
* Encode parameters.
*
* @param parameters the parameters
* @return the string
*/
public static String encodeQuery(Map<String, String> parameters){
public static String encodeParameters(Map<String, String> parameters){
String query = "";