Commit d309c974 authored by Carlos Juliano Viana's avatar Carlos Juliano Viana
Browse files

Merge branch 'task/CSBASE-4917_ReviseApiRestDemo_INFOGRD-3126' into 'master'

[CASBASE-4917] Atualização da DEMO Java de acesso REST ao servidor CSBase

See merge request !1
parents bacc41bc ed881668
# Uso da API REST do CSBase
## Documentação da API REST do CSGrid que serve ao Portal ERAS
- [Documentação Swagger da API](https://eras.tecgraf.puc-rio.br/workingserver/docs/?url=https://eras.tecgraf.puc-rio.br/workingserver/v1/swagger.yaml#/)
## Demonstrações
### JavaScript
Execução de um algoritmo (Packgen): https://plnkr.co/edit/0xqIoNX3nLu3l8A7yd7r
obs: o exemplo em JavaScript não é análogo ao exemplo Java.
### Java
Demonstração da execução do algoritmo de "Zip Utility" (zip) através da API REST de um servidor CSGrid.
Condições para a demo executar:
- algoritmo zip cadastrado
- alguma maquina remota disponível para executar
- ter um usuário já cadastrado e com as permissões necessárias para execução do algoritmo zip nas máquinas disponíveis
- projeto previamente criado
- usuário é o dono do projeto ou tem permissão de escrita no projeto
- arquivos de entrada disponiveis localmente
- caminho válido para arquivo de saída a ser criado localmente
O código fonte da demo está disponível em https://git.tecgraf.puc-rio.br/csbase-dev/rest-client-demo-java.
Para melhor entendimento do código fonte e da parametrização de um algoritmo, pode ser útil ler as seguintes referências:
- [Parametrização de um algoritmo para execução](https://jira.tecgraf.puc-rio.br/confluence/pages/viewpage.action?pageId=36504138)
- [Manual de Criação de Configuradores para Algoritmos do CSGrid](http://webserver2.tecgraf.puc-rio.br/ftp_pub/csbase/1.5.5/manualConfiguradorXML.pdf)
A demo está disponível como um jar executável no Maven público do Tecgraf (http://maven.tecgraf.puc-rio.br:8081/nexus/)
```
<dependency>
<groupId>br.puc-rio.tecgraf.csbase</groupId>
<artifactId>rest-client-demo-java</artifactId>
<version>1.0.0</version>
<classifier>jar-with-dependencies</classifier>
</dependency>
```
Para executar, basta criar um arquivo de configuração e passar como parâmetro.
Exemplo de arquivo de configuração:
**config.properties**
```
# Host do servidor CSGrid usado na demo
host http://localhost:8010/v1/
# Login de usuário existente
username tester
# Senha do usuário
password tester
# Projeto usado para execução remota
project teste
# Pasta do projeto onde os arquivos de entrada e saída serão gerados
projectFolderPath root
# Primeiro arquivo local que será transferido para o projeto e que é entrada do algoritmo
input_file_1 input1.txt
# Segundo arquivo local que será transferido para o projeto e que é entrada do algoritmo
input_file_2 input2.txt
# Arquivo local que será criado com o conteúdo lido do arquivo resultado da execução do algoritmo
output_file output.zip
```
Exemplo de execução com o arquivo de configuração acima:
```
java -jar rest-client-demo-java-1.0.0-jar-with-dependencies.jar config.properties
```
A saída de demo é a seguinte:
```
-- Inicio da demo
Propriedades carregadas de src/main/resources/config.properties
Projeto tester/teste encontrado
Algoritmo zip encontrado
Algoritmo zip submetido com sucesso
Job test@test.BBNB3I5AHL encontrado
Job state (1548177277184): EXECUTING
Job state (1548177277231): DOWNLOADING
Job state (1548177278761): FINISHED
Job test@test.BBNB3I5AHL terminou
Resultado em input1.txt:
Breve arquivo de exemplo de entrada para ser enviado ao servidor.
Resultado em output.zip: 1882 bytes
Resultado em OUTROoutput.zip: 1882 bytes
```
OBS: O jobId (test@test.BBNB3I5AHL) será sempre único a cada execução. O valor dentro dos parêntesis após "Job state" é sempre o timestamp em milissegundos.
Para executar, a partir do código fonte, usando o Maven:
```
mvn exec:java -Dexec.args="config.properties"
```
......@@ -4,7 +4,7 @@
<groupId>br.puc-rio.tecgraf.csbase</groupId>
<artifactId>rest-client-demo-java</artifactId>
<version>0.0.1</version>
<version>1.0.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>resttest</name>
......
......@@ -8,20 +8,38 @@ import Requests.Authentication.LoginOrPasswordNotProvidedException;
import Requests.Authentication.Token;
import Requests.Jobs.JobInfo;
import Requests.Jobs.JobServices;
import Requests.Projects.*;
import Requests.Projects.FileExplorer;
import Requests.Projects.PermissionException;
import Requests.Projects.Project;
import Requests.Projects.ProjectExplorer;
import Requests.Projects.ProjectOrFileNotFoundException;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
/**
* Exemplo de submissão programática da execução de um algoritmo em um servidor
* CSBase.
*
* Compatível com a versão 1.4.6 do iBase.
*
* @author Tecgraf/PUC-Rio
*/
public class CommandSubmissionClient {
private static final String ALGORITHM = "multmatrix";
private static final String REMOTE_INPUT_FILE_1 = "data_1.in";
private static final String REMOTE_INPUT_FILE_2 = "data_2.in";
private static final String REMOTE_OUTPUT_FILE = "result.out";
private static final String ALGORITHM = "zip";
private static final String REMOTE_INPUT_FILE_1 = "input1.txt";
private static final String REMOTE_INPUT_FILE_2 = "input2.txt";
private static final String REMOTE_OUTPUT_FILE = "output.zip";
/**
* Nome do arquivo com as propriedades para a demo
......@@ -33,7 +51,7 @@ public class CommandSubmissionClient {
* - usuário já cadastrado
* - projeto previamente criado
* - usuário é o dono do projeto
* - algoritmo multmatrix cadastrado
* - algoritmo zip cadastrado
* - arquivos de entrada disponiveis localmente
* - caminho válido para arquivo de saída a ser criado localmente
* - alguma maquina remota disponivel para executar
......@@ -69,37 +87,37 @@ public class CommandSubmissionClient {
System.exit(1);
}
String username = props.getProperty("username");
if (host==null || host.isEmpty()) {
if (username == null || username.isEmpty()) {
System.out.println ("A propriedade username e' obrigatoria");
System.exit(1);
}
String password = props.getProperty("password");
if (host==null || host.isEmpty()) {
if (password == null || password.isEmpty()) {
System.out.println ("A propriedade password e' obrigatoria");
System.exit(1);
}
String projectName = username + "/" + props.getProperty("project");
if (host==null || host.isEmpty()) {
if (projectName.equals(username + "/" + null) || projectName.equals(username + "/")) {
System.out.println ("A propriedade project e' obrigatoria");
System.exit(1);
}
String projectFolderPath = props.getProperty("projectFolderPath");
if (host==null || host.isEmpty()) {
if (projectFolderPath == null || projectFolderPath.isEmpty()) {
System.out.println ("A propriedade projectFolderPath e' obrigatoria");
System.exit(1);
}
String toUploadFilePath1 = props.getProperty("input_file_1");
if (host==null || host.isEmpty()) {
if (toUploadFilePath1 == null || toUploadFilePath1.isEmpty()) {
System.out.println ("A propriedade input_file_1 e' obrigatoria");
System.exit(1);
}
String toUploadFilePath2 = props.getProperty("input_file_2");
if (host==null || host.isEmpty()) {
if (toUploadFilePath2 == null || toUploadFilePath2.isEmpty()) {
System.out.println ("A propriedade input_file_2 e' obrigatoria");
System.exit(1);
}
String toSaveFilePath = props.getProperty("output_file");
if (host==null || host.isEmpty()) {
if (toSaveFilePath == null || toSaveFilePath.isEmpty()) {
System.out.println ("A propriedade output_file e' obrigatoria");
System.exit(1);
}
......@@ -132,17 +150,36 @@ public class CommandSubmissionClient {
/*
* Faz o upload dos dados de entrada na raiz do projeto
*
* Os arquivos que serão necessários para o algoritmo precisam ser enviados para o projeto, pois é de
* lá onde o algoritmo consegue ler os arquivos.
*/
FileExplorer.uploadFile(host, token, project, projectFolderPath, toUploadFilePath1, REMOTE_INPUT_FILE_1);
FileExplorer.uploadFile(host, token, project, projectFolderPath, toUploadFilePath2, REMOTE_INPUT_FILE_2);
/*
* Executa o algoritmo Multmatrix
* Cria a lista que será utilizada como parâmetro do algoritmo.
*
* O algoritmo Zip recebe como parâmetro de entrada uma lista de arquivos (que
* pode conter somente um arquivo). No exemplo, utilizamos dois arquivos.
*/
Map<String, String> commandArgs = new HashMap<>();
commandArgs.put("matrix1", REMOTE_INPUT_FILE_1);
commandArgs.put("matrix2", REMOTE_INPUT_FILE_2);
commandArgs.put("result", REMOTE_OUTPUT_FILE);
List<String> inputFiles = new ArrayList<>();
inputFiles.add(REMOTE_INPUT_FILE_1);
inputFiles.add(REMOTE_INPUT_FILE_2);
/*
* O nome dos parâmetros (aqui no exemplo "ENTRADA" e "SAIDA") são definidos pelo próprio algoritmo, dentro de seu arquivo config.xml
* Acesse os links abaixo para mais informações sobre o config.xml
* https://jira.tecgraf.puc-rio.br/confluence/pages/viewpage.action?pageId=36504138
* http://webserver2.tecgraf.puc-rio.br/ftp_pub/csbase/1.5.5/manualConfiguradorXML.pdf
*/
Map<String, Object> commandArgs = new HashMap<>();
commandArgs.put("ENTRADA", inputFiles);
commandArgs.put("SAIDA", REMOTE_OUTPUT_FILE);
/*
* Executa o algoritmo Zip (obtido previamente)
*/
String jobId = JobServices.submitJob(host, token, project, algorithm, algorithm.getVersions().get(0),
"demo rest java", 0, false, new String[0], commandArgs);
if (jobId==null) {
......@@ -151,6 +188,9 @@ public class CommandSubmissionClient {
}
System.out.println("Algoritmo " + ALGORITHM + " submetido com sucesso");
/*
* Obtém as informações do job criado para a execução do algoritmo.
*/
JobInfo jinfo = JobServices.getJobInfo(host, token, jobId);
if (jinfo==null) {
System.out.println("Job "+ jobId + " nao foi encontrado");
......@@ -158,21 +198,44 @@ public class CommandSubmissionClient {
}
System.out.println("Job " + jobId + " encontrado");
/**
* Aguarda o job terminar sua execução
*/
/*
* Aguarda o job terminar sua execução
*/
JobServices.awaitJobEnd(host, token, jinfo);
/**
* Faz download do arquivo de saída (quando o arquivo é pequeno)
*/
String result = FileExplorer.downloadFile(host, token, project, projectFolderPath, REMOTE_OUTPUT_FILE);
System.out.println("Resultado em " + REMOTE_OUTPUT_FILE + ":\n"+result);
/*
* Faz download do arquivo (quando o arquivo é pequeno e somente
* texto)
*
* obs: como o arquivo de saída é um binário (zip), esse exemplo está
* sendo feito com um dos arquivos de entrada que foi enviado para o
* servidor, mas basta alterar o nome do arquivo na chamada para obter o
* arquivo pretendido.
*/
String textResult = FileExplorer.downloadTextFile(host, token, project, projectFolderPath, REMOTE_INPUT_FILE_1);
System.out.println("\nResultado em " + REMOTE_INPUT_FILE_1 + ":\n" + textResult);
/*
* Faz download de arquivo. (quando o arquivo é pequeno e qualquer formato)
*
* obs: neste caso fazemos download do arquivo de saída.
*/
InputStream fileStream = FileExplorer.downloadBinaryFile(host, token, project, projectFolderPath, REMOTE_OUTPUT_FILE);
long fileSize = Files.copy(fileStream, new File(toSaveFilePath).toPath(), StandardCopyOption.REPLACE_EXISTING);
fileStream.close();
/**
System.out.println("\nResultado em " + REMOTE_OUTPUT_FILE + ": " + fileSize + " bytes ");
/*
* Faz download do arquivo de saída (quando o arquivo é grande)
*
* obs: colocamos um prefixo no nome do arquivo a ser salvo para diferenciar do arquivo obtido no exemplo acima.
*/
FileExplorer.downloadLargeFile(host, token, project, projectFolderPath, REMOTE_OUTPUT_FILE, toSaveFilePath);
FileExplorer.downloadLargeFile(host, token, project, projectFolderPath, REMOTE_OUTPUT_FILE, "OUTRO" + toSaveFilePath);
System.out.println("\nResultado em " + "OUTRO" + toSaveFilePath + ": " + new File("OUTRO" + toSaveFilePath).length() + " bytes ");
} catch (LoginOrPasswordNotProvidedException e) {
e.printStackTrace();
......@@ -182,6 +245,8 @@ public class CommandSubmissionClient {
e.printStackTrace();
} catch (PermissionException e) {
e.printStackTrace();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
......@@ -9,7 +9,7 @@ import java.util.Objects;
public class Algorithm {
private String id = null;
private String name = null;
private String description = null;
private List<String> categories = null;
private User whoCreated = null;
private AlgorithmVersion lastVersion = null;
private List<AlgorithmVersion> versions = new ArrayList<AlgorithmVersion>();
......@@ -33,15 +33,18 @@ public class Algorithm {
public void setName(String name) {
this.name = name;
}
public Algorithm description(String description) {
this.description = description;
public Algorithm categories(List<String> categories) {
this.categories = categories;
return this;
}
public String getDescription() {
return description;
public List<String> getCategories() {
return categories;
}
public void setDescription(String description) {
this.description = description;
public void setCategories(List<String> categories) {
this.categories = categories;
}
public Algorithm whoCreated(User whoCreated) {
this.whoCreated = whoCreated;
......@@ -89,7 +92,7 @@ public class Algorithm {
Algorithm algorithm = (Algorithm) o;
return Objects.equals(this.id, algorithm.id) &&
Objects.equals(this.name, algorithm.name) &&
Objects.equals(this.description, algorithm.description) &&
Objects.equals(this.categories, algorithm.categories) &&
Objects.equals(this.whoCreated, algorithm.whoCreated) &&
Objects.equals(this.lastVersion, algorithm.lastVersion) &&
Objects.equals(this.versions, algorithm.versions);
......@@ -97,7 +100,7 @@ public class Algorithm {
@Override
public int hashCode() {
return Objects.hash(id, name, description, whoCreated, lastVersion, versions);
return Objects.hash(id, name, categories, whoCreated, lastVersion, versions);
}
@Override
......@@ -106,7 +109,7 @@ public class Algorithm {
sb.append("class Algorithm {\n");
sb.append(" id: ").append(toIndentedString(id)).append("\n");
sb.append(" name: ").append(toIndentedString(name)).append("\n");
sb.append(" description: ").append(toIndentedString(description)).append("\n");
sb.append(" categories: ").append(toIndentedString(categories)).append("\n");
sb.append(" whoCreated: ").append(toIndentedString(whoCreated)).append("\n");
sb.append(" lastVersion: ").append(toIndentedString(lastVersion)).append("\n");
sb.append(" versions: ").append(toIndentedString(versions)).append("\n");
......
This diff is collapsed.
......@@ -19,7 +19,7 @@ public class JobServices {
public static String submitJob(String host, Token token, Project project, Algorithm algorithm,
AlgorithmVersion version, String description, int priority, boolean emailOnTerminated,
String[] candidateMachines, Map<String, String> args) {
String[] candidateMachines, Map<String, Object> args) {
Client client = ClientBuilder.newClient();
WebTarget webTarget = client.target(host).path("jobs");
......@@ -30,6 +30,7 @@ public class JobServices {
postBody.put("args", args);
postBody.put("priority", Integer.toString(priority));
postBody.put("emailOnTerminated", Boolean.toString(emailOnTerminated));
postBody.put("numberOfJobs", new Integer(1));
Invocation.Builder invocationBuilder = webTarget.request("application/json;charset=UTF-8");
Response response = invocationBuilder
......
package Requests.Jobs;
import java.util.Objects;
import java.util.ArrayList;
import java.util.List;
/**
* MonitoredFile
*/
public class MonitoredFile {
private String name = null;
private String id = null;
private List<String> formats = new ArrayList<String>();
public MonitoredFile name(String name) {
this.name = name;
return this;
}
/**
* Get name
* @return name
**/
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public MonitoredFile id(String id) {
this.id = id;
return this;
}
/**
* Get id
* @return id
**/
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public MonitoredFile formats(List<String> formats) {
this.formats = formats;
return this;
}
public MonitoredFile addFormatsItem(String formatsItem) {
this.formats.add(formatsItem);
return this;
}
/**
* Get formats
* @return formats
**/
public List<String> getFormats() {
return formats;
}
public void setFormats(List<String> formats) {
this.formats = formats;
}
@Override
public boolean equals(java.lang.Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
MonitoredFile monitoredFile = (MonitoredFile) o;
return Objects.equals(this.name, monitoredFile.name) &&
Objects.equals(this.id, monitoredFile.id) &&
Objects.equals(this.formats, monitoredFile.formats);
}
@Override
public int hashCode() {
return Objects.hash(name, id, formats);
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("class MonitoredFile {\n");
sb.append(" name: ").append(toIndentedString(name)).append("\n");
sb.append(" id: ").append(toIndentedString(id)).append("\n");
sb.append(" formats: ").append(toIndentedString(formats)).append("\n");
sb.append("}");
return sb.toString();
}
/**
* Convert the given object to string with each line indented by 4 spaces
* (except the first line).
*/
private String toIndentedString(java.lang.Object o) {
if (o == null) {
return "null";
}
return o.toString().replace("\n", "\n ");
}
}
......@@ -16,6 +16,7 @@ import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.channels.Channels;
......@@ -76,7 +77,7 @@ public class FileExplorer {
return str;
}
public static String downloadFile(String host, Token token, Project project, String projectFolderPath,
public static String downloadTextFile(String host, Token token, Project project, String projectFolderPath,
String fileName)
throws ProjectOrFileNotFoundException, PermissionException, InvalidLoginOrPasswordException {
Client client = ClientBuilder.newClient();
......@@ -104,6 +105,34 @@ public class FileExplorer {
return null;
}
public static InputStream downloadBinaryFile(String host, Token token, Project project, String projectFolderPath,
String fileName)
throws ProjectOrFileNotFoundException, PermissionException, InvalidLoginOrPasswordException {
Client client = ClientBuilder.newClient();
WebTarget webTarget = client.target(host);
String fileId = Base64.getEncoder().encodeToString(projectFolderPath.equals("root") ? fileName.getBytes()
: (projectFolderPath + (fileName == null ? "" : "/" + fileName)).getBytes());
WebTarget uploadTarget = webTarget.path("projects").path(project.getId()).path("files").path(fileId);
Invocation.Builder invocationBuilder = uploadTarget.request();
Response response = invocationBuilder
.header(HttpHeaders.AUTHORIZATION, token.getTokenType() + token.getAccessToken()).get();
int status = response.getStatus();
if (status == Response.Status.OK.getStatusCode())
return response.readEntity(InputStream.class);
else if (status == Response.Status.BAD_REQUEST.getStatusCode())
throw new InvalidLoginOrPasswordException();
else if (status == Response.Status.FORBIDDEN.getStatusCode())
throw new PermissionException();
else if (status == Response.Status.NOT_FOUND.getStatusCode())
throw new ProjectOrFileNotFoundException();
client.close();
return null;
}
public static void downloadLargeFile(String host, Token token, Project project, String projectFolderPath,
String fileName, String destinFileName)
throws ProjectOrFileNotFoundException, PermissionException, InvalidLoginOrPasswordException {
......@@ -125,7 +154,9 @@ public class FileExplorer {
URL website;
try {
File file = new File(destinFileName);
file.getParentFile().mkdirs();
if (file.getParentFile() != null) {
file.getParentFile().mkdirs();
}
file.createNewFile();
website = new URL(link.getUrl());
ReadableByteChannel rbc = Channels.newChannel(website.openStream());
......
......@@ -2,8 +2,8 @@
#
# - usuário já cadastrado e com as perissões necessárias para execução
# - projeto previamente criado
# - usuário é o dono do projeto
# - algoritmo multmatrix cadastrado
# - usuário é o dono do projeto ou tem permissão de escrita nele
# - algoritmo zip cadastrado
# - arquivos de entrada disponiveis localmente
# - caminho válido para arquivo de saída a ser criado localmente
# - alguma maquina remota disponivel para executar
......@@ -11,16 +11,16 @@
# Host do servidor CSGrid usado na demo