storagehub/src/main/java/org/gcube/data/access/storagehub/services/admin/ScriptManager.java

201 lines
6.5 KiB
Java

package org.gcube.data.access.storagehub.services.admin;
import static org.gcube.data.access.storagehub.Roles.INFRASTRUCTURE_MANAGER_ROLE;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.PrintWriter;
import java.io.StringReader;
import java.io.StringWriter;
import javax.inject.Inject;
import javax.jcr.Node;
import javax.servlet.ServletContext;
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import org.apache.cxf.io.ReaderInputStream;
import org.apache.jackrabbit.api.JackrabbitSession;
import org.gcube.common.authorization.control.annotations.AuthorizationControl;
import org.gcube.common.authorization.library.AuthorizedTasks;
import org.gcube.common.authorization.library.provider.AuthorizationProvider;
import org.gcube.common.storagehub.model.Paths;
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.exception.MyAuthException;
import org.gcube.data.access.storagehub.handlers.CredentialHandler;
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.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;
@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.repository;
@Inject
AccountingHandler accountingHandler;
@Context
ServletContext context;
@Inject
ScriptUtil scriptUtil;
@Inject
ItemHandler itemHandler;
@Inject
PathUtil pathUtil;
@POST
@Path("execute")
@AuthorizationControl(allowedRoles = {INFRASTRUCTURE_MANAGER_ROLE},exception=MyAuthException.class)
@Consumes(MediaType.MULTIPART_FORM_DATA)
public String run( @FormDataParam("name") String name,
@FormDataParam("asynch") Boolean asynch,
@FormDataParam("destinationFolderId") String destinationFolderId,
@FormDataParam("file") InputStream stream,
@FormDataParam("file") FormDataContentDisposition fileDetail) {
try {
ScriptClassLoader scriptClassLoader = new ScriptClassLoader(Thread.currentThread().getContextClassLoader());
Class<?> scriptClass = uploadClass(stream, scriptClassLoader, fileDetail.getFileName().replace(".class", ""));
return run(scriptClass, name, destinationFolderId, asynch!=null? asynch : false);
}catch(Throwable e) {
log.error("error executing script {}", name,e);
throw new WebApplicationException("error loading class",e);
}
}
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 String run(Class<?> clazz, String name, String destinationFolderId, boolean asynch) throws Throwable {
String login = AuthorizationProvider.instance.get().getClient().getId();
log.info("script {} called by {}", clazz.getSimpleName(), login);
JackrabbitSession ses = null;
try {
ses = (JackrabbitSession) repository.getRepository().login(CredentialHandler.getAdminCredentials(context));
String parentId = destinationFolderId!=null ? destinationFolderId : ses.getNode(pathUtil.getWorkspacePath(login).toPath()).getIdentifier();
Node parentNode = ses.getNodeByIdentifier(parentId);
String parentPath = parentNode.getPath();
if (AbstractScript.class.isAssignableFrom(clazz)) {
AbstractScript scriptInstance = (AbstractScript) clazz.newInstance();
RealRun realRun = new RealRun(ses, scriptInstance, login, parentId, name);
if (asynch) {
new Thread(AuthorizedTasks.bind(realRun)).start();
}else realRun.run();
} else throw new Exception("class "+clazz.getSimpleName()+" not implements AbstractScript");
return Paths.append(Paths.getPath(parentPath), name).toPath();
}catch (Throwable e) {
if (ses !=null && ses.isLive())
ses.logout();
throw e;
}
}
class RealRun implements Runnable{
private JackrabbitSession ses;
AbstractScript instance;
String login;
String parentId;
String name;
public RealRun(JackrabbitSession ses, AbstractScript instance, String login, String parentId, String name) {
super();
this.ses = ses;
this.instance = instance;
this.login = login;
this.parentId = parentId;
this.name = name;
}
@Override
public void run() {
try{
String result ="";
try {
result = instance.run(ses, null, scriptUtil);
log.info("result is {}",result);
}catch(Throwable t) {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw, true);
t.printStackTrace(pw);
result+= "\n"+sw.toString();
}
try( InputStream stream = new ReaderInputStream(new StringReader(result))){
ItemsParameterBuilder<FileCreationParameters> builder = FileCreationParameters.builder().name(name).description("result of script execution "+name)
.stream(stream).on(parentId).with(ses).author(login);
itemHandler.create(builder.build());
} catch (Throwable e) {
log.error("error saving script result {} in the Workspace",name, e);
}
} finally {
if (ses!=null)
ses.logout();
}
}
}
class ScriptClassLoader extends ClassLoader{
public ScriptClassLoader(ClassLoader parent) {
super(parent);
}
public Class<?> findClass(String name, byte[] b) {
return defineClass(name, b, 0, b.length);
}
}
}