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; @Path("admin/script") 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 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); } } }