268 lines
8.9 KiB
Java
268 lines
8.9 KiB
Java
package org.gcube.data.access.storagehub.services.admin;
|
|
|
|
import static org.gcube.data.access.storagehub.Roles.INFRASTRUCTURE_MANAGER_ROLE;
|
|
|
|
import java.io.ByteArrayInputStream;
|
|
import java.io.ByteArrayOutputStream;
|
|
import java.io.InputStream;
|
|
import java.io.PrintWriter;
|
|
import java.io.StringWriter;
|
|
import java.util.HashMap;
|
|
import java.util.UUID;
|
|
|
|
import javax.jcr.Node;
|
|
import javax.jcr.Session;
|
|
|
|
import org.apache.jackrabbit.api.JackrabbitSession;
|
|
import org.gcube.common.authorization.control.annotations.AuthorizationControl;
|
|
import org.gcube.common.security.AuthorizedTasks;
|
|
import org.gcube.common.security.providers.SecretManagerProvider;
|
|
import org.gcube.common.storagehub.model.Paths;
|
|
import org.gcube.data.access.storagehub.Constants;
|
|
import org.gcube.data.access.storagehub.PathUtil;
|
|
import org.gcube.data.access.storagehub.StorageHubAppllicationManager;
|
|
import org.gcube.data.access.storagehub.accounting.AccountingHandler;
|
|
import org.gcube.data.access.storagehub.handlers.items.ItemHandler;
|
|
import org.gcube.data.access.storagehub.handlers.items.builders.FileCreationParameters;
|
|
import org.gcube.data.access.storagehub.handlers.items.builders.ItemsParameterBuilder;
|
|
import org.gcube.data.access.storagehub.scripting.AbstractScript;
|
|
import org.gcube.data.access.storagehub.scripting.ScriptUtil;
|
|
import org.gcube.data.access.storagehub.services.RepositoryInitializer;
|
|
import org.gcube.data.access.storagehub.services.admin.ScriptStatus.Status;
|
|
import org.gcube.smartgears.ContextProvider;
|
|
import org.gcube.smartgears.context.application.ApplicationContext;
|
|
import org.gcube.smartgears.utils.InnerMethodName;
|
|
import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
|
|
import org.glassfish.jersey.media.multipart.FormDataParam;
|
|
import org.slf4j.Logger;
|
|
import org.slf4j.LoggerFactory;
|
|
|
|
import com.webcohesion.enunciate.metadata.rs.RequestHeader;
|
|
import com.webcohesion.enunciate.metadata.rs.RequestHeaders;
|
|
|
|
import jakarta.inject.Inject;
|
|
import jakarta.servlet.ServletContext;
|
|
import jakarta.ws.rs.Consumes;
|
|
import jakarta.ws.rs.DefaultValue;
|
|
import jakarta.ws.rs.GET;
|
|
import jakarta.ws.rs.POST;
|
|
import jakarta.ws.rs.Path;
|
|
import jakarta.ws.rs.PathParam;
|
|
import jakarta.ws.rs.Produces;
|
|
import jakarta.ws.rs.WebApplicationException;
|
|
import jakarta.ws.rs.core.Context;
|
|
import jakarta.ws.rs.core.MediaType;
|
|
|
|
@Path("admin/script")
|
|
@RequestHeaders({
|
|
@RequestHeader( name = "Authorization", description = "Bearer token, see https://dev.d4science.org/how-to-access-resources"),
|
|
})
|
|
public class ScriptManager {
|
|
|
|
|
|
private static Logger log = LoggerFactory.getLogger(ScriptManager.class);
|
|
|
|
private RepositoryInitializer repository = StorageHubAppllicationManager.getRepository();
|
|
|
|
@Inject
|
|
AccountingHandler accountingHandler;
|
|
|
|
@Context
|
|
ServletContext context;
|
|
|
|
@Inject
|
|
ScriptUtil scriptUtil;
|
|
|
|
@Inject
|
|
ItemHandler itemHandler;
|
|
|
|
@Inject
|
|
PathUtil pathUtil;
|
|
|
|
private static HashMap<String, ScriptStatus> scriptStatusMap = new HashMap<String, ScriptStatus>();
|
|
|
|
@POST
|
|
@Path("execute")
|
|
@AuthorizationControl(allowedRoles = {INFRASTRUCTURE_MANAGER_ROLE})
|
|
@Consumes(MediaType.MULTIPART_FORM_DATA)
|
|
@Produces(MediaType.APPLICATION_JSON)
|
|
public ScriptStatus run( @FormDataParam("name") String name,
|
|
@FormDataParam("asynch") @DefaultValue("false") Boolean asynch,
|
|
@FormDataParam("writeResult") @DefaultValue("false") Boolean writeResult ,
|
|
@FormDataParam("destinationFolderId") String destinationFolderId,
|
|
@FormDataParam("file") InputStream stream,
|
|
@FormDataParam("file") FormDataContentDisposition fileDetail) {
|
|
try {
|
|
InnerMethodName.set("executeScript");
|
|
ScriptClassLoader scriptClassLoader = new ScriptClassLoader(Thread.currentThread().getContextClassLoader());
|
|
Class<?> scriptClass = uploadClass(stream, scriptClassLoader, fileDetail.getFileName().replace(".class", ""));
|
|
return internalRun(scriptClass, name, destinationFolderId, asynch, writeResult);
|
|
}catch(Throwable e) {
|
|
log.error("error executing script {}", name,e);
|
|
throw new WebApplicationException("error loading class",e);
|
|
}
|
|
}
|
|
|
|
@GET
|
|
@Path("{id}/status")
|
|
@AuthorizationControl(allowedRoles = {INFRASTRUCTURE_MANAGER_ROLE})
|
|
@Produces(MediaType.APPLICATION_JSON)
|
|
public ScriptStatus getStatus(@PathParam("id") String runningId) {
|
|
InnerMethodName.set("getScriptStatus");
|
|
if (!scriptStatusMap.containsKey(runningId)) {
|
|
log.error("script with id {} not found",runningId);
|
|
throw new WebApplicationException("id "+runningId+" not found", 404);
|
|
}
|
|
ScriptStatus status = scriptStatusMap.get(runningId);
|
|
if (status.getStatus()!= Status.Running)
|
|
scriptStatusMap.remove(runningId);
|
|
return status;
|
|
}
|
|
|
|
|
|
private Class<?> uploadClass(InputStream stream, ScriptClassLoader classLoader, String name) throws Throwable {
|
|
try(ByteArrayOutputStream buffer = new ByteArrayOutputStream()){
|
|
int nRead;
|
|
byte[] data = new byte[1024];
|
|
while ((nRead = stream.read(data, 0, data.length)) != -1)
|
|
buffer.write(data, 0, nRead);
|
|
|
|
buffer.flush();
|
|
byte[] byteArray = buffer.toByteArray();
|
|
return classLoader.findClass(name, byteArray);
|
|
}
|
|
|
|
}
|
|
|
|
private ScriptStatus internalRun(Class<?> clazz, String name, String destinationFolderId, boolean asynch, boolean writeResult) throws Throwable {
|
|
String login = SecretManagerProvider.get().getOwner().getId();
|
|
log.info("script {} called by {}", clazz.getSimpleName(), login);
|
|
try {
|
|
|
|
|
|
String resultPath= null;
|
|
Node parentNode = null;
|
|
|
|
if (writeResult) {
|
|
JackrabbitSession ses = null;
|
|
try {
|
|
ses = (JackrabbitSession) repository.getRepository().login(Constants.JCR_CREDENTIALS);
|
|
String parentId = destinationFolderId!=null ? destinationFolderId : ses.getNode(pathUtil.getWorkspacePath(login).toPath()).getIdentifier();
|
|
parentNode = ses.getNodeByIdentifier(parentId);
|
|
resultPath = Paths.append(Paths.getPath(parentNode.getPath()), name).toPath();
|
|
}finally {
|
|
if (ses!=null)
|
|
ses.logout();
|
|
}
|
|
}
|
|
|
|
if (AbstractScript.class.isAssignableFrom(clazz)) {
|
|
AbstractScript scriptInstance = (AbstractScript) clazz.getDeclaredConstructor().newInstance();
|
|
|
|
ApplicationContext appContext = ContextProvider.get();
|
|
String serverHost = appContext.container().configuration().hostname();
|
|
|
|
|
|
String runningId = UUID.randomUUID().toString();
|
|
ScriptStatus status = new ScriptStatus(runningId, resultPath, serverHost);
|
|
|
|
RealRun realRun = new RealRun(scriptInstance, login, parentNode, name, writeResult, status);
|
|
if (asynch) {
|
|
scriptStatusMap.put(runningId, status);
|
|
new Thread(AuthorizedTasks.bind(realRun)).start();
|
|
return status;
|
|
}else {
|
|
realRun.run();
|
|
return status;
|
|
}
|
|
|
|
} else throw new Exception("class "+clazz.getSimpleName()+" not implements AbstractScript");
|
|
|
|
|
|
|
|
}catch (Throwable e) {
|
|
throw e;
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
class RealRun implements Runnable{
|
|
|
|
AbstractScript instance;
|
|
String login;
|
|
Node parentNode;
|
|
String name;
|
|
boolean writeResult = true;
|
|
ScriptStatus status;
|
|
|
|
public RealRun(AbstractScript instance, String login, Node parentNode, String name, boolean writeResult, ScriptStatus status) {
|
|
super();
|
|
this.instance = instance;
|
|
this.login = login;
|
|
this.parentNode = parentNode;
|
|
this.name = name;
|
|
this.writeResult = writeResult;
|
|
this.status = status;
|
|
}
|
|
|
|
|
|
@Override
|
|
public void run() {
|
|
String result ="";
|
|
try {
|
|
JackrabbitSession executeSession = null;
|
|
try {
|
|
executeSession = (JackrabbitSession) repository.getRepository().login(Constants.JCR_CREDENTIALS);
|
|
result = instance.run(executeSession, null, scriptUtil);
|
|
status.setSuccess();
|
|
}catch(Throwable t) {
|
|
StringWriter sw = new StringWriter();
|
|
PrintWriter pw = new PrintWriter(sw, true);
|
|
t.printStackTrace(pw);
|
|
status.setFailed(sw.toString());
|
|
result+= "\n"+sw.toString();
|
|
log.warn("error executing script {}",instance.getClass().getSimpleName(), t);
|
|
}finally {
|
|
if (executeSession !=null && executeSession.isLive())
|
|
executeSession.logout();
|
|
}
|
|
|
|
if (this.writeResult) {
|
|
Session writeSession = null;
|
|
try( InputStream stream = new ByteArrayInputStream(result.getBytes())){
|
|
writeSession = repository.getRepository().login(Constants.JCR_CREDENTIALS);
|
|
ItemsParameterBuilder<FileCreationParameters> builder = FileCreationParameters.builder().name(name).description("result of script execution "+name)
|
|
.stream(stream).on(parentNode.getIdentifier()).with(writeSession).author(login);
|
|
itemHandler.create(builder.build());
|
|
} catch (Throwable e) {
|
|
log.error("error saving script result {} in the Workspace",name, e);
|
|
} finally {
|
|
if (writeSession!=null)
|
|
writeSession.logout();
|
|
}
|
|
}
|
|
}catch (Exception e) {
|
|
log.error("unexpected error executing script {}",instance.getClass().getSimpleName(),e);
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
class ScriptClassLoader extends ClassLoader{
|
|
|
|
public ScriptClassLoader(ClassLoader parent) {
|
|
super(parent);
|
|
}
|
|
|
|
public Class<?> findClass(String name, byte[] b) {
|
|
return defineClass(name, b, 0, b.length);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|