diff --git a/deploy.sh b/deploy.sh new file mode 100755 index 000000000..c40765366 --- /dev/null +++ b/deploy.sh @@ -0,0 +1 @@ +cp vip-portal/target/vip-portal-2.6-SNAPSHOT-local.war /home/cornier/workspace/tools/apache-tomcat-9.0.70/webapps/ROOT.war diff --git a/vip-application/src/main/java/fr/insalyon/creatis/vip/application/Application.gwt.xml b/vip-application/src/main/java/fr/insalyon/creatis/vip/application/Application.gwt.xml index 6b0b9a61a..297ac333f 100644 --- a/vip-application/src/main/java/fr/insalyon/creatis/vip/application/Application.gwt.xml +++ b/vip-application/src/main/java/fr/insalyon/creatis/vip/application/Application.gwt.xml @@ -40,7 +40,9 @@ knowledge of the CeCILL-B license and that you accept its terms. + + diff --git a/vip-application/src/main/java/fr/insalyon/creatis/vip/application/client/ApplicationConstants.java b/vip-application/src/main/java/fr/insalyon/creatis/vip/application/client/ApplicationConstants.java index 320ede5d6..1c1b02480 100644 --- a/vip-application/src/main/java/fr/insalyon/creatis/vip/application/client/ApplicationConstants.java +++ b/vip-application/src/main/java/fr/insalyon/creatis/vip/application/client/ApplicationConstants.java @@ -46,6 +46,7 @@ public class ApplicationConstants { public final static String TAB_MANAGE_ENGINE = "manage_engine_tab"; public final static String TAB_MONITOR = "monitor_tab"; public final static String TAB_STATS = "stats_tab"; + public final static String TAB_REPROVIP = "reproVip_tab"; // Icons private static final String IMG_FOLDER = "application/"; public static final String ICON_APPLICATION = IMG_FOLDER + "icon-application.png"; @@ -107,6 +108,7 @@ public class ApplicationConstants { public static final String APP_SIMULATION_ERROR = "Error File"; public static final String APP_SIMULATION_OUT = "Output File"; public static final String APP_PUBLIC_APPLICATION = "Applications"; + public static final String APP_REPRO_VIP = "ReproVIP"; // Application Images public static final String APP_IMG_APPLICATION = IMG_FOLDER + "app-application.png"; public static final String APP_IMG_CLASSES = IMG_FOLDER + "app-classes.png"; diff --git a/vip-application/src/main/java/fr/insalyon/creatis/vip/application/client/rpc/ExecutionsService.java b/vip-application/src/main/java/fr/insalyon/creatis/vip/application/client/rpc/ExecutionsService.java new file mode 100644 index 000000000..2e978b7d3 --- /dev/null +++ b/vip-application/src/main/java/fr/insalyon/creatis/vip/application/client/rpc/ExecutionsService.java @@ -0,0 +1,25 @@ +package fr.insalyon.creatis.vip.application.client.rpc; + +import com.google.gwt.core.client.GWT; +import com.google.gwt.user.client.rpc.RemoteService; +import com.google.gwt.user.client.rpc.ServiceDefTarget; +import fr.insalyon.creatis.vip.application.client.view.ApplicationException; +import fr.insalyon.creatis.vip.core.client.bean.Execution; + +import java.util.List; + +public interface ExecutionsService extends RemoteService { + + public static final String SERVICE_URI = "/executionsservice"; + + public static class Util { + public static ExecutionsServiceAsync getInstance() { + ExecutionsServiceAsync instance = (ExecutionsServiceAsync) GWT.create(ExecutionsService.class); + ServiceDefTarget target = (ServiceDefTarget) instance; + target.setServiceEntryPoint(GWT.getModuleBaseURL() + SERVICE_URI); + return instance; + } + } + public List getExecutions() throws ApplicationException; +} + diff --git a/vip-application/src/main/java/fr/insalyon/creatis/vip/application/client/rpc/ExecutionsServiceAsync.java b/vip-application/src/main/java/fr/insalyon/creatis/vip/application/client/rpc/ExecutionsServiceAsync.java new file mode 100644 index 000000000..eac7e337a --- /dev/null +++ b/vip-application/src/main/java/fr/insalyon/creatis/vip/application/client/rpc/ExecutionsServiceAsync.java @@ -0,0 +1,10 @@ +package fr.insalyon.creatis.vip.application.client.rpc; + +import com.google.gwt.user.client.rpc.AsyncCallback; +import fr.insalyon.creatis.vip.core.client.bean.Execution; + +import java.util.List; + +public interface ExecutionsServiceAsync { + void getExecutions(AsyncCallback> callback); +} diff --git a/vip-application/src/main/java/fr/insalyon/creatis/vip/application/client/rpc/ReproVipService.java b/vip-application/src/main/java/fr/insalyon/creatis/vip/application/client/rpc/ReproVipService.java new file mode 100644 index 000000000..be9f5d793 --- /dev/null +++ b/vip-application/src/main/java/fr/insalyon/creatis/vip/application/client/rpc/ReproVipService.java @@ -0,0 +1,31 @@ +package fr.insalyon.creatis.vip.application.client.rpc; + +import com.google.gwt.core.client.GWT; +import com.google.gwt.user.client.rpc.RemoteService; +import com.google.gwt.user.client.rpc.ServiceDefTarget; +import fr.insalyon.creatis.vip.application.client.view.ApplicationException; +import fr.insalyon.creatis.vip.core.client.bean.Execution; +import fr.insalyon.creatis.vip.core.client.bean.User; +import fr.insalyon.creatis.vip.core.client.view.CoreException; + +public interface ReproVipService extends RemoteService { + + public static final String SERVICE_URI = "/reprovipservice"; + + public static class Util { + + public static ReproVipServiceAsync getInstance() { + ReproVipServiceAsync instance = (ReproVipServiceAsync) GWT.create(ReproVipService.class); + ServiceDefTarget target = (ServiceDefTarget) instance; + target.setServiceEntryPoint(GWT.getModuleBaseURL() + SERVICE_URI); + return instance; + } + } + public void executionAdminEmail(Execution execution); + void addExecution(Execution execution) throws CoreException; + void updateExecution(String executionID, String newStatus) throws CoreException; + boolean doesExecutionExist(String executionID) throws CoreException; + String createReproVipDirectory(String executionName, String executionID, String version, String comments) throws CoreException; + void deleteReproVipDirectory(String executionID) throws CoreException; +} + diff --git a/vip-application/src/main/java/fr/insalyon/creatis/vip/application/client/rpc/ReproVipServiceAsync.java b/vip-application/src/main/java/fr/insalyon/creatis/vip/application/client/rpc/ReproVipServiceAsync.java new file mode 100644 index 000000000..b91872599 --- /dev/null +++ b/vip-application/src/main/java/fr/insalyon/creatis/vip/application/client/rpc/ReproVipServiceAsync.java @@ -0,0 +1,14 @@ +package fr.insalyon.creatis.vip.application.client.rpc; + +import com.google.gwt.user.client.rpc.AsyncCallback; +import fr.insalyon.creatis.vip.core.client.bean.Execution; + +public interface ReproVipServiceAsync { + void executionAdminEmail(Execution execution, AsyncCallback callback); + void addExecution(Execution execution, AsyncCallback asyncCallback); + void updateExecution(String executionID, String newStatus, AsyncCallback asyncCallback); + void doesExecutionExist(String executionID, AsyncCallback asyncCallback); + void createReproVipDirectory(String executionName, String execution, String version, String comments, AsyncCallback callback); + void deleteReproVipDirectory(String executionID, AsyncCallback callback); +} + diff --git a/vip-application/src/main/java/fr/insalyon/creatis/vip/application/client/rpc/WorkflowService.java b/vip-application/src/main/java/fr/insalyon/creatis/vip/application/client/rpc/WorkflowService.java index 31a9d8c41..edcb65c58 100644 --- a/vip-application/src/main/java/fr/insalyon/creatis/vip/application/client/rpc/WorkflowService.java +++ b/vip-application/src/main/java/fr/insalyon/creatis/vip/application/client/rpc/WorkflowService.java @@ -66,6 +66,7 @@ public static WorkflowServiceAsync getInstance() { // public Descriptor getApplicationDescriptor(String applicationName, String applicationVersion) throws ApplicationException; + String getRawApplicationDescriptorPath(String applicationName, String applicationVersion) throws ApplicationException; public String getApplicationDescriptorString(String applicationName, String applicationVersion) throws ApplicationException; public void launchSimulation(Map parameters, String applicationName, String applicationVersion, String applicationClass, String simulationName) throws ApplicationException; diff --git a/vip-application/src/main/java/fr/insalyon/creatis/vip/application/client/rpc/WorkflowServiceAsync.java b/vip-application/src/main/java/fr/insalyon/creatis/vip/application/client/rpc/WorkflowServiceAsync.java index fbc13d31d..b733891fd 100644 --- a/vip-application/src/main/java/fr/insalyon/creatis/vip/application/client/rpc/WorkflowServiceAsync.java +++ b/vip-application/src/main/java/fr/insalyon/creatis/vip/application/client/rpc/WorkflowServiceAsync.java @@ -48,6 +48,7 @@ public interface WorkflowServiceAsync { public void getSimulations(Date lastDate, AsyncCallback> asyncCallback); // public void getApplicationDescriptor(String applicationName, String applicationVersion, AsyncCallback asyncCallback); + public void getRawApplicationDescriptorPath(String applicationName, String applicationVersion, AsyncCallback callback); public void getApplicationDescriptorString(String applicationName, String applicationVersion, AsyncCallback asyncCallback); public void launchSimulation(Map parameters, String applicationName, String applicationVersion, String applicationClass, String simulationName, AsyncCallback asyncCallback); diff --git a/vip-application/src/main/java/fr/insalyon/creatis/vip/application/client/view/ApplicationHomeParser.java b/vip-application/src/main/java/fr/insalyon/creatis/vip/application/client/view/ApplicationHomeParser.java index 8b40eafa9..1fd5ef0d5 100644 --- a/vip-application/src/main/java/fr/insalyon/creatis/vip/application/client/view/ApplicationHomeParser.java +++ b/vip-application/src/main/java/fr/insalyon/creatis/vip/application/client/view/ApplicationHomeParser.java @@ -35,6 +35,7 @@ import fr.insalyon.creatis.vip.application.client.ApplicationConstants; import fr.insalyon.creatis.vip.application.client.view.monitor.SimulationsTab; import fr.insalyon.creatis.vip.application.client.view.system.application.ManageApplicationsTab; +import fr.insalyon.creatis.vip.application.client.view.system.application.ReproVipTab; import fr.insalyon.creatis.vip.core.client.CoreModule; import fr.insalyon.creatis.vip.core.client.bean.User; import fr.insalyon.creatis.vip.core.client.view.CoreConstants; @@ -56,6 +57,9 @@ public void loadApplications() { addApplication(ApplicationConstants.APP_PUBLIC_APPLICATION, ApplicationConstants.APP_IMG_APPLICATION); + + addApplication(ApplicationConstants.APP_REPRO_VIP, + ApplicationConstants.APP_IMG_APPLICATION); } @Override @@ -72,6 +76,12 @@ public boolean parse(String applicationName, String applicationVersion) { () -> new ManageApplicationsTab(true)); return true; } + if (applicationName.equals(ApplicationConstants.APP_REPRO_VIP)) { + Layout.getInstance().addTab( + ApplicationConstants.TAB_REPROVIP, + () -> new ReproVipTab()); + return true; + } return false; } } diff --git a/vip-application/src/main/java/fr/insalyon/creatis/vip/application/client/view/monitor/ViewerWindow.java b/vip-application/src/main/java/fr/insalyon/creatis/vip/application/client/view/monitor/ViewerWindow.java index 35c7d78a1..214c799aa 100644 --- a/vip-application/src/main/java/fr/insalyon/creatis/vip/application/client/view/monitor/ViewerWindow.java +++ b/vip-application/src/main/java/fr/insalyon/creatis/vip/application/client/view/monitor/ViewerWindow.java @@ -180,9 +180,33 @@ public void onClick(ClickEvent event) { } }); toolStrip.addButton(downloadButton); + + if (!file) { + ToolStripButton jsonDownloadButton = new ToolStripButton(); + jsonDownloadButton.setTitle("Download JSON"); + jsonDownloadButton.addClickHandler(new ClickHandler() { + public void onClick(ClickEvent event) { + downloadJsonContent(); + } + }); + toolStrip.addButton(jsonDownloadButton); + } } private void loadString() { pane.setContents(content.replaceAll("<", "<").replaceAll(">", ">").replaceAll("\n", "
")); } + + private void downloadJsonContent() { + String jsonContent = this.content; + String mimeType = "application/json;charset=utf-8;"; + String blobData = "data:" + mimeType + ", " + encodeURIComponent(jsonContent); + com.google.gwt.user.client.Window.open(blobData, "_blank", "Download JSON"); + } + + // This is a helper method for encoding the content + private static native String encodeURIComponent(String content) /*-{ + return encodeURIComponent(content); +}-*/; + } diff --git a/vip-application/src/main/java/fr/insalyon/creatis/vip/application/client/view/monitor/menu/SimulationsContextMenu.java b/vip-application/src/main/java/fr/insalyon/creatis/vip/application/client/view/monitor/menu/SimulationsContextMenu.java index 5b060aa80..77d16b9aa 100644 --- a/vip-application/src/main/java/fr/insalyon/creatis/vip/application/client/view/monitor/menu/SimulationsContextMenu.java +++ b/vip-application/src/main/java/fr/insalyon/creatis/vip/application/client/view/monitor/menu/SimulationsContextMenu.java @@ -31,6 +31,7 @@ */ package fr.insalyon.creatis.vip.application.client.view.monitor.menu; +import com.google.gwt.core.client.GWT; import com.google.gwt.user.client.rpc.AsyncCallback; import com.smartgwt.client.util.BooleanCallback; import com.smartgwt.client.util.SC; @@ -40,18 +41,23 @@ import com.smartgwt.client.widgets.menu.events.ClickHandler; import com.smartgwt.client.widgets.menu.events.MenuItemClickEvent; import fr.insalyon.creatis.vip.application.client.ApplicationConstants; +import fr.insalyon.creatis.vip.application.client.rpc.ReproVipService; +import fr.insalyon.creatis.vip.application.client.rpc.ReproVipServiceAsync; import fr.insalyon.creatis.vip.application.client.rpc.WorkflowService; import fr.insalyon.creatis.vip.application.client.view.common.AbstractSimulationTab; -import fr.insalyon.creatis.vip.application.client.view.launch.LaunchTab; import fr.insalyon.creatis.vip.application.client.view.launch.RelaunchService; import fr.insalyon.creatis.vip.application.client.view.monitor.ChangeSimulationUserLayout; import fr.insalyon.creatis.vip.application.client.view.monitor.SimulationStatus; import fr.insalyon.creatis.vip.application.client.view.monitor.SimulationTab; import fr.insalyon.creatis.vip.application.client.view.monitor.SimulationsTab; import fr.insalyon.creatis.vip.core.client.CoreModule; +import fr.insalyon.creatis.vip.core.client.bean.Execution; import fr.insalyon.creatis.vip.core.client.view.CoreConstants; import fr.insalyon.creatis.vip.core.client.view.ModalWindow; import fr.insalyon.creatis.vip.core.client.view.layout.Layout; +import fr.insalyon.creatis.vip.application.client.view.system.application.MakeExecutionPublicTab; +import fr.insalyon.creatis.vip.core.server.business.BusinessException; + import java.util.Map; /** @@ -67,10 +73,11 @@ public class SimulationsContextMenu extends Menu { private String applicationVersion; private String applicationClass; private String simulationUser; + private ReproVipServiceAsync reproVipServiceAsync = ReproVipService.Util.getInstance(); public SimulationsContextMenu(ModalWindow modal, final String simulationID, - final String title, final SimulationStatus status, String applicationName, - String applicationVersion, String applicationClass, String simulationUser) { + final String title, final SimulationStatus status, String applicationName, + String applicationVersion, String applicationClass, String simulationUser) { this.modal = modal; this.simulationID = simulationID; @@ -90,8 +97,8 @@ public SimulationsContextMenu(ModalWindow modal, final String simulationID, @Override public void onClick(MenuItemClickEvent event) { Layout.getInstance().addTab( - AbstractSimulationTab.tabIdFrom(simulationID), - () -> new SimulationTab(simulationID, title, status)); + AbstractSimulationTab.tabIdFrom(simulationID), + () -> new SimulationTab(simulationID, title, status)); } }); @@ -181,28 +188,57 @@ public void onClick(MenuItemClickEvent event) { } }); + MenuItem makePublicExecutionItem = new MenuItem("Make this execution public"); + makePublicExecutionItem.addClickHandler(new ClickHandler() { + @Override + public void onClick(MenuItemClickEvent event) { + reproVipServiceAsync.doesExecutionExist(simulationID, new AsyncCallback() { + @Override + public void onFailure(Throwable caught) { + SC.warn("Error checking if execution exists: " + caught.getMessage()); + } + @Override + public void onSuccess(Boolean exists) { + if (exists) { + SC.warn("This execution already exists and cannot be made public again."); + } else { + SC.ask("Do you really want to make this execution public: (" + title + ")?", new BooleanCallback() { + @Override + public void execute(Boolean value) { + if (value) { + Execution execution = new Execution(simulationID, simulationName, applicationName, applicationVersion, "Initializer", simulationUser, "comments"); + Layout.getInstance().addTab(CoreConstants.TAB_MAKE_EXECUTION_PUBLIC, () -> new MakeExecutionPublicTab(execution)); + } + } + }); + } + } + }); + } + }); + MenuItemSeparator separator = new MenuItemSeparator(); switch (status) { case Running: if (CoreModule.user.isSystemAdministrator()) { - this.setItems(viewItem, killItem, separator, relauchItem, separator, changeUserItem); + this.setItems(viewItem, killItem, separator, relauchItem, separator, changeUserItem, separator, makePublicExecutionItem); } else { - this.setItems(viewItem, killItem, separator, relauchItem); + this.setItems(viewItem, killItem, separator, relauchItem, separator, makePublicExecutionItem); } break; case Completed: if (CoreModule.user.isSystemAdministrator()) { - this.setItems(viewItem, cleanItem, separator, relauchItem, separator, changeUserItem); + this.setItems(viewItem, cleanItem, separator, relauchItem, separator, changeUserItem, separator, makePublicExecutionItem); } else { - this.setItems(viewItem, cleanItem, separator, relauchItem); + this.setItems(viewItem, cleanItem, separator, relauchItem, separator, makePublicExecutionItem); } break; case Cleaned: if (CoreModule.user.isSystemAdministrator()) { - this.setItems(viewItem, purgeItem, separator, changeUserItem); + this.setItems(viewItem, purgeItem, separator, changeUserItem, separator, makePublicExecutionItem); } else { this.setItems(viewItem); } @@ -211,9 +247,9 @@ public void onClick(MenuItemClickEvent event) { case Failed: case Killed: if (CoreModule.user.isSystemAdministrator()) { - this.setItems(viewItem, markCompletedItem, cleanItem, separator, relauchItem, separator, changeUserItem); + this.setItems(viewItem, markCompletedItem, cleanItem, separator, relauchItem, separator, changeUserItem, separator, makePublicExecutionItem); } else { - this.setItems(viewItem, cleanItem, separator, relauchItem); + this.setItems(viewItem, cleanItem, separator, relauchItem, separator, makePublicExecutionItem); } } } @@ -318,7 +354,7 @@ public void onFailure(Throwable caught) { public void onSuccess(final Map result) { modal.hide(); String tabId = - ApplicationConstants.getLaunchTabID(applicationName); + ApplicationConstants.getLaunchTabID(applicationName); Layout.getInstance().removeTab(tabId); RelaunchService.getInstance().relaunch( applicationName, applicationVersion, applicationClass, simulationName, result, tabId); diff --git a/vip-application/src/main/java/fr/insalyon/creatis/vip/application/client/view/system/application/ExecutionsContextMenu.java b/vip-application/src/main/java/fr/insalyon/creatis/vip/application/client/view/system/application/ExecutionsContextMenu.java new file mode 100644 index 000000000..7a14f0362 --- /dev/null +++ b/vip-application/src/main/java/fr/insalyon/creatis/vip/application/client/view/system/application/ExecutionsContextMenu.java @@ -0,0 +1,101 @@ +package fr.insalyon.creatis.vip.application.client.view.system.application; + +import com.google.gwt.user.client.rpc.AsyncCallback; +import com.smartgwt.client.util.SC; +import com.smartgwt.client.widgets.menu.Menu; +import com.smartgwt.client.widgets.menu.MenuItem; +import com.smartgwt.client.widgets.menu.events.ClickHandler; +import com.smartgwt.client.widgets.menu.events.MenuItemClickEvent; +import fr.insalyon.creatis.vip.application.client.rpc.ReproVipService; +import fr.insalyon.creatis.vip.application.client.rpc.ReproVipServiceAsync; +import fr.insalyon.creatis.vip.core.client.view.CoreConstants; +import fr.insalyon.creatis.vip.core.client.view.ModalWindow; + +public class ExecutionsContextMenu extends Menu { + private ModalWindow modal; + private String executionID; + private ReproVipServiceAsync reproVipServiceAsync = ReproVipService.Util.getInstance(); + private ReproVipService reproVipService; + + public ExecutionsContextMenu(ModalWindow modal, String executionName, String executionID, String version, String status, String comments) { + this.modal = modal; + this.executionID = executionID; + this.setShowShadow(true); + this.setShadowDepth(10); + this.setWidth(90); + + MenuItem OptionPublicExecutionItem = new MenuItem("Option make it public"); + OptionPublicExecutionItem.setIcon(CoreConstants.ICON_SUCCESS); + OptionPublicExecutionItem.addClickHandler(new ClickHandler() { + @Override + public void onClick(MenuItemClickEvent event) { + if (!status.equals("ReproVIP directory created")) { + createReproVipDirectory(executionName, executionID, version, comments); + makexExecutionPublic("ReproVIP directory created"); + } else { + SC.warn("ReproVIP directory is already created for this execution."); + } + } + }); + + MenuItem DeleteExecutionItem = new MenuItem("Delete ReproVIP directory"); + DeleteExecutionItem.setIcon(CoreConstants.ICON_CLEAR); + DeleteExecutionItem.addClickHandler(new ClickHandler() { + @Override + public void onClick(MenuItemClickEvent event) { + if (!status.equals("Initializer")) { + deleteReproVipDirectory(executionID); + makexExecutionPublic("Initializer"); + } else { + SC.warn("ReproVIP directory is not yet created"); + } + } + }); + + this.setItems(OptionPublicExecutionItem, DeleteExecutionItem); + } + + private void makexExecutionPublic(String newStatus) { + final AsyncCallback callback = new AsyncCallback() { + @Override + public void onFailure(Throwable caught) { + modal.hide(); + SC.warn("Unable to make this execution public:
" + caught.getMessage()); + } + + @Override + public void onSuccess(Void result) { + modal.hide(); + SC.say("Execution made public successfully"); + } + }; + modal.show("Make execution public", true); + reproVipServiceAsync.updateExecution(executionID, newStatus, callback); + } + + public void createReproVipDirectory(String executionName, String executionID, String version, String comments) { + reproVipServiceAsync.createReproVipDirectory(executionName, executionID, version, comments, new AsyncCallback() { + public void onFailure(Throwable caught) { + SC.warn("Error creating ReproVip directory: " + caught.getMessage()); + } + + @Override + public void onSuccess(String s) { + SC.say("ReproVip directory successfully created"); + } + }); + } + public void deleteReproVipDirectory(String executionID) { + reproVipServiceAsync.deleteReproVipDirectory(executionID, new AsyncCallback() { + @Override + public void onFailure(Throwable caught) { + SC.warn("Error deleting ReproVip directory: " + caught.getMessage()); + } + + @Override + public void onSuccess(String s) { + SC.say("ReproVip directory successfully deleted"); + } + }); + } +} diff --git a/vip-application/src/main/java/fr/insalyon/creatis/vip/application/client/view/system/application/ExecutionsLayout.java b/vip-application/src/main/java/fr/insalyon/creatis/vip/application/client/view/system/application/ExecutionsLayout.java new file mode 100644 index 000000000..2a6f8f228 --- /dev/null +++ b/vip-application/src/main/java/fr/insalyon/creatis/vip/application/client/view/system/application/ExecutionsLayout.java @@ -0,0 +1,91 @@ +package fr.insalyon.creatis.vip.application.client.view.system.application; + +import com.google.gwt.event.shared.HandlerRegistration; +import com.google.gwt.user.client.rpc.AsyncCallback; +import com.smartgwt.client.types.Overflow; +import com.smartgwt.client.widgets.grid.ListGrid; +import com.smartgwt.client.widgets.grid.ListGridField; +import com.smartgwt.client.widgets.grid.ListGridRecord; +import com.smartgwt.client.widgets.grid.events.RowContextClickEvent; +import com.smartgwt.client.widgets.grid.events.RowContextClickHandler; +import com.smartgwt.client.widgets.layout.VLayout; +import fr.insalyon.creatis.vip.application.client.rpc.ExecutionsService; +import fr.insalyon.creatis.vip.core.client.bean.Execution; +import fr.insalyon.creatis.vip.core.client.view.ModalWindow; +import fr.insalyon.creatis.vip.core.client.view.layout.Layout; +import java.util.ArrayList; +import java.util.List; + +public class ExecutionsLayout extends VLayout { + private ModalWindow modal; + private ListGrid grid; + protected HandlerRegistration rowContextClickHandler; + + public ExecutionsLayout() { + + this.setWidth100(); + this.setHeight100(); + this.setOverflow(Overflow.AUTO); + + configureGrid(); + modal = new ModalWindow(grid); + loadData(); + } + + private void configureGrid() { + + grid = new ListGrid(); + grid.setWidth100(); + grid.setHeight100(); + grid.setShowAllRecords(false); + grid.setShowRowNumbers(true); + grid.setShowEmptyMessage(true); + grid.setEmptyMessage("
No data available."); + grid.setFields( + new ListGridField("id", "Execution ID"), + new ListGridField("simulation_name", "Execution Simulation Name"), + new ListGridField("application_name", "Execution Application Name"), + new ListGridField("version", "Version"), + new ListGridField("status", "Status"), + new ListGridField("author", "Author"), + new ListGridField("comments", "Comments")); + grid.addRowContextClickHandler(new RowContextClickHandler() { + @Override + public void onRowContextClick(RowContextClickEvent event) { + event.cancel(); + ListGridRecord selectedRecord = grid.getSelectedRecord(); + String executionName = selectedRecord.getAttribute("application_name"); + String executionId = selectedRecord.getAttribute("id"); + String version = selectedRecord.getAttribute("version"); + String status = selectedRecord.getAttribute("status"); + String comments = selectedRecord.getAttribute("comments"); + new ExecutionsContextMenu(modal, executionName, executionId, version, status, comments).showContextMenu(); + } + }); + + this.addMember(grid); + } + + public void loadData() { + final AsyncCallback> callback = new AsyncCallback>() { + @Override + public void onFailure(Throwable caught) { + modal.hide(); + Layout.getInstance().setWarningMessage("Unable to get list of executions:
" + caught.getMessage()); + } + + @Override + public void onSuccess(List result) { + modal.hide(); + List dataList = new ArrayList(); + + for (Execution exe : result) { + dataList.add(new ExecutionsRecord(exe.getId(), exe.getSimulationName(), exe.getApplicationName(), exe.getVersion(), exe.getStatus(), exe.getAuthor(), exe.getComments())); + } + grid.setData(dataList.toArray(new ExecutionsRecord[]{})); + } + }; + modal.show("Loading executions...", true); + ExecutionsService.Util.getInstance().getExecutions(callback); + } +} diff --git a/vip-application/src/main/java/fr/insalyon/creatis/vip/application/client/view/system/application/ExecutionsRecord.java b/vip-application/src/main/java/fr/insalyon/creatis/vip/application/client/view/system/application/ExecutionsRecord.java new file mode 100644 index 000000000..b7ba4bf41 --- /dev/null +++ b/vip-application/src/main/java/fr/insalyon/creatis/vip/application/client/view/system/application/ExecutionsRecord.java @@ -0,0 +1,17 @@ +package fr.insalyon.creatis.vip.application.client.view.system.application; + +import com.smartgwt.client.widgets.grid.ListGridRecord; + +public class ExecutionsRecord extends ListGridRecord { + public ExecutionsRecord(String id, String simulationName, String applicationName, String version, String status, String author, String comments) { + setAttribute("id", id); + setAttribute("simulation_name", simulationName); + setAttribute("application_name", applicationName); + setAttribute("version", version); + setAttribute("status", status); + setAttribute("author", author); + setAttribute("comments", comments); + } +} + + diff --git a/vip-application/src/main/java/fr/insalyon/creatis/vip/application/client/view/system/application/MakeExecutionPublicTab.java b/vip-application/src/main/java/fr/insalyon/creatis/vip/application/client/view/system/application/MakeExecutionPublicTab.java new file mode 100644 index 000000000..9662d417e --- /dev/null +++ b/vip-application/src/main/java/fr/insalyon/creatis/vip/application/client/view/system/application/MakeExecutionPublicTab.java @@ -0,0 +1,185 @@ +package fr.insalyon.creatis.vip.application.client.view.system.application; + +import com.google.gwt.user.client.rpc.AsyncCallback; +import com.google.gwt.user.client.ui.PopupPanel; +import com.smartgwt.client.types.Alignment; +import com.smartgwt.client.types.Overflow; +import com.smartgwt.client.util.SC; +import com.smartgwt.client.widgets.IButton; +import com.smartgwt.client.widgets.Label; +import com.smartgwt.client.widgets.events.ClickEvent; +import com.smartgwt.client.widgets.events.ClickHandler; +import com.smartgwt.client.widgets.form.DynamicForm; +import com.smartgwt.client.widgets.form.fields.*; +import com.smartgwt.client.widgets.layout.VLayout; +import com.smartgwt.client.widgets.tab.Tab; +import fr.insalyon.creatis.vip.application.client.bean.boutiquesTools.BoutiquesApplication; +import fr.insalyon.creatis.vip.application.client.bean.boutiquesTools.BoutiquesOutputFile; +import fr.insalyon.creatis.vip.application.client.rpc.*; +import fr.insalyon.creatis.vip.application.client.view.boutiquesParsing.BoutiquesParser; +import fr.insalyon.creatis.vip.application.client.view.boutiquesParsing.InvalidBoutiquesDescriptorException; +import fr.insalyon.creatis.vip.core.client.bean.Execution; +import fr.insalyon.creatis.vip.core.client.view.CoreConstants; +import fr.insalyon.creatis.vip.core.client.view.layout.Layout; +import fr.insalyon.creatis.vip.core.client.view.util.FieldUtil; +import fr.insalyon.creatis.vip.core.client.view.util.WidgetUtil; + +import java.util.*; + +public class MakeExecutionPublicTab extends Tab { + private VLayout makeExecutionPublicLayout; + private TextItem idOfTheExecutionField; + private TextItem nameOfTheExecutionSimulationField; + private TextItem nameOfTheExecutionApplicationField; + private TextItem versionExecutionField; + private TextItem statusExecutionField; + private TextItem authorNameField; + private TextAreaItem commentsItem; + private IButton makeExecutionblicButton; + private DynamicForm outputFilesForm; + private ReproVipServiceAsync reproVipServiceAsync = ReproVipService.Util.getInstance(); + public MakeExecutionPublicTab(Execution execution) { + + this.setID(CoreConstants.TAB_MAKE_EXECUTION_PUBLIC); + this.setTitle("Make execution public"); + this.setCanClose(true); + + VLayout vLayout = new VLayout(); + vLayout.setWidth100(); + vLayout.setHeight100(); + vLayout.setMargin(5); + vLayout.setOverflow(Overflow.AUTO); + vLayout.setDefaultLayoutAlign(Alignment.CENTER); + + outputFilesForm = new DynamicForm(); + Layout.getInstance().getModal().show("Loading...", true); + AsyncCallback descriptorCallback = new AsyncCallback() { + @Override + public void onFailure(Throwable caught) { + Layout.getInstance().getModal().hide(); + SC.warn("Error retrieving application descriptor: " + caught.getMessage()); + } + @Override + public void onSuccess(String descriptorString) { + try { + BoutiquesApplication applicationTool = new BoutiquesParser().parseApplication(descriptorString); + if(applicationTool.getOutputFiles() == null || applicationTool.getOutputFiles().isEmpty()) { + SC.warn("Output File is empty"); + } else { + Set outputFiles = applicationTool.getOutputFiles(); + if (outputFiles != null && !outputFiles.isEmpty()) { + CheckboxItem[] checkboxes = new CheckboxItem[outputFiles.size()]; + int index = 0; + for (BoutiquesOutputFile outputFile : outputFiles) { + CheckboxItem checkbox = new CheckboxItem(outputFile.getName(), outputFile.getName()); + checkboxes[index] = checkbox; + index++; + } + outputFilesForm.setFields(checkboxes); + } + } + } catch (InvalidBoutiquesDescriptorException exception) { + SC.warn("Error when parsing application descriptor: " + exception.getMessage()); + } finally { + Layout.getInstance().getModal().hide(); + } + } + }; + + WorkflowService.Util.getInstance().getApplicationDescriptorString(execution.getApplicationName(), execution.getVersion(), descriptorCallback); + + configureExecutionPublicLayout(); + idOfTheExecutionField.setValue(execution.getId()); + nameOfTheExecutionSimulationField.setValue(execution.getSimulationName()); + nameOfTheExecutionApplicationField.setValue(execution.getApplicationName()); + versionExecutionField.setValue(execution.getVersion()); + statusExecutionField.setValue(execution.getStatus()); + authorNameField.setValue(execution.getAuthor()); + commentsItem.setValue(execution.getComments()); + vLayout.addMember(makeExecutionPublicLayout); + + this.setPane(vLayout); + } + + private void configureExecutionPublicLayout() { + + idOfTheExecutionField = FieldUtil.getTextItem(300, false, "", null); + idOfTheExecutionField.setCanEdit(false); + + nameOfTheExecutionSimulationField = FieldUtil.getTextItem(300, false, "", null); + + nameOfTheExecutionApplicationField = FieldUtil.getTextItem(300, false, "", null); + nameOfTheExecutionApplicationField.setCanEdit(false); + + authorNameField = FieldUtil.getTextItem(300, false, "", null); + + versionExecutionField= FieldUtil.getTextItem(300, false, "", null); + versionExecutionField.setCanEdit(false); + + statusExecutionField = FieldUtil.getTextItem(300, false, "", null); + statusExecutionField.setCanEdit(false); + + commentsItem = new TextAreaItem("comment", ""); + commentsItem.setHeight(80); + commentsItem.setWidth(300); + commentsItem.setShowTitle(false); + + outputFilesForm = new DynamicForm(); + + makeExecutionblicButton = new IButton("Make execution public"); + + makeExecutionblicButton.addClickHandler(new ClickHandler() { + public void onClick(ClickEvent event) { + String id = idOfTheExecutionField.getValueAsString(); + String nameSimulation = nameOfTheExecutionSimulationField.getValueAsString(); + String nameApplication = nameOfTheExecutionApplicationField.getValueAsString(); + String version = versionExecutionField.getValueAsString(); + String status = statusExecutionField.getValueAsString(); + String author = authorNameField.getValueAsString(); + String comments = commentsItem.getValueAsString(); + + Execution newExecution = new Execution(id, nameSimulation, nameApplication, version, status, author, comments); + reproVipServiceAsync.addExecution(newExecution, new AsyncCallback() { + public void onFailure(Throwable caught) { + SC.warn("Failed to add execution: " + caught.getMessage()); + } + public void onSuccess(Void result) { + sendExecutionAdminEmail(newExecution); + SC.say("Execution added successfully!"); + } + }); + } + }); + + Label descriptionLabel = new Label("Choose your output file(s):"); + descriptionLabel.setHeight(20); + descriptionLabel.setWidth100(); + + makeExecutionPublicLayout = WidgetUtil.getVIPLayout(320); + WidgetUtil.addFieldToVIPLayout(makeExecutionPublicLayout, "ID of the execution", idOfTheExecutionField); + WidgetUtil.addFieldToVIPLayout(makeExecutionPublicLayout, "Name of the execution simulation", nameOfTheExecutionSimulationField); + WidgetUtil.addFieldToVIPLayout(makeExecutionPublicLayout, "Name of the execution application", nameOfTheExecutionApplicationField); + WidgetUtil.addFieldToVIPLayout(makeExecutionPublicLayout, "Version of the execution", versionExecutionField); + WidgetUtil.addFieldToVIPLayout(makeExecutionPublicLayout, "Status of the execution", statusExecutionField); + WidgetUtil.addFieldToVIPLayout(makeExecutionPublicLayout, "Author name", authorNameField); + WidgetUtil.addFieldToVIPLayout(makeExecutionPublicLayout, "Comments", commentsItem); + makeExecutionPublicLayout.addMember(descriptionLabel); + makeExecutionPublicLayout.addMember(outputFilesForm); + makeExecutionPublicLayout.addMember(makeExecutionblicButton); + } + + private void sendExecutionAdminEmail(Execution execution) { + final AsyncCallback callback = new AsyncCallback() { + @Override + public void onFailure(Throwable caught) { + Layout.getInstance().setWarningMessage("Unable to send email to admins:
" + caught.getMessage()); + } + + @Override + public void onSuccess(Void result) { + Layout.getInstance().setNoticeMessage("Email has been sent to admins."); + } + }; + reproVipServiceAsync.executionAdminEmail(execution, callback); + } +} diff --git a/vip-application/src/main/java/fr/insalyon/creatis/vip/application/client/view/system/application/ReproVipTab.java b/vip-application/src/main/java/fr/insalyon/creatis/vip/application/client/view/system/application/ReproVipTab.java new file mode 100644 index 000000000..9ed2ece1f --- /dev/null +++ b/vip-application/src/main/java/fr/insalyon/creatis/vip/application/client/view/system/application/ReproVipTab.java @@ -0,0 +1,31 @@ +package fr.insalyon.creatis.vip.application.client.view.system.application; + +import com.smartgwt.client.widgets.Label; +import com.smartgwt.client.widgets.layout.HLayout; +import com.smartgwt.client.widgets.layout.VLayout; +import com.smartgwt.client.widgets.tab.Tab; +import fr.insalyon.creatis.vip.application.client.ApplicationConstants; +import fr.insalyon.creatis.vip.core.client.view.common.AbstractManageTab; +import fr.insalyon.creatis.vip.core.client.view.util.WidgetUtil; + + +public class ReproVipTab extends AbstractManageTab { + private ExecutionsLayout executionsLayout; + + public ReproVipTab() { + + super(ApplicationConstants.ICON_APPLICATION, ApplicationConstants.APP_REPRO_VIP, ApplicationConstants.TAB_REPROVIP); + + executionsLayout = new ExecutionsLayout(); + + HLayout appLayout = new HLayout(5); + appLayout.setHeight("50%"); + appLayout.addMember(executionsLayout); + vLayout.addMember(appLayout); + } + + public void loadApplications() { + executionsLayout.loadData(); + } + +} diff --git a/vip-application/src/main/java/fr/insalyon/creatis/vip/application/server/business/ExecutionInOutData.java b/vip-application/src/main/java/fr/insalyon/creatis/vip/application/server/business/ExecutionInOutData.java new file mode 100644 index 000000000..e1e457cd4 --- /dev/null +++ b/vip-application/src/main/java/fr/insalyon/creatis/vip/application/server/business/ExecutionInOutData.java @@ -0,0 +1,29 @@ +package fr.insalyon.creatis.vip.application.server.business; + +import fr.insalyon.creatis.vip.application.client.bean.InOutData; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +@Service +@Transactional +public class ExecutionInOutData { + private List inputData; + private List outputData; + public ExecutionInOutData(List inputData, List outputData) { + this.inputData = inputData; + this.outputData = outputData; + } + public List getInputData() { + return inputData; + } + public void setInputData(List inputData) { + this.inputData = inputData; + } + public List getOutputData() { + return outputData; + } + public void setOutputData(List outputData) { + this.outputData = outputData; + } +} diff --git a/vip-application/src/main/java/fr/insalyon/creatis/vip/application/server/business/ExecutionJobTaskData.java b/vip-application/src/main/java/fr/insalyon/creatis/vip/application/server/business/ExecutionJobTaskData.java new file mode 100644 index 000000000..df00a7e08 --- /dev/null +++ b/vip-application/src/main/java/fr/insalyon/creatis/vip/application/server/business/ExecutionJobTaskData.java @@ -0,0 +1,19 @@ +package fr.insalyon.creatis.vip.application.server.business; + + +import fr.insalyon.creatis.vip.application.client.bean.Task; + +import java.util.List; +import java.util.Map; + +public class ExecutionJobTaskData { + private final List jobs; + public ExecutionJobTaskData(List jobs) { + this.jobs = jobs; + } + public List getJobs() { + return jobs; + } +} + + diff --git a/vip-application/src/main/java/fr/insalyon/creatis/vip/application/server/business/ExecutionsBusiness.java b/vip-application/src/main/java/fr/insalyon/creatis/vip/application/server/business/ExecutionsBusiness.java new file mode 100644 index 000000000..2139cde1c --- /dev/null +++ b/vip-application/src/main/java/fr/insalyon/creatis/vip/application/server/business/ExecutionsBusiness.java @@ -0,0 +1,31 @@ +package fr.insalyon.creatis.vip.application.server.business; + +import fr.insalyon.creatis.vip.core.client.bean.Execution; +import fr.insalyon.creatis.vip.core.server.business.BusinessException; +import fr.insalyon.creatis.vip.core.server.dao.DAOException; +import fr.insalyon.creatis.vip.core.server.dao.ExecutionPublicDAO; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +@Service +@Transactional +public class ExecutionsBusiness { + private ExecutionPublicDAO executionPublicDAO; + + @Autowired + public ExecutionsBusiness(ExecutionPublicDAO executionPublicDAO) { + this.executionPublicDAO = executionPublicDAO; + } + + public List getExecutions() throws BusinessException { + try { + return executionPublicDAO.getExecutions(); + } catch (DAOException ex) { + throw new BusinessException(ex); + } + } +} + diff --git a/vip-application/src/main/java/fr/insalyon/creatis/vip/application/server/business/ReproVipBusiness.java b/vip-application/src/main/java/fr/insalyon/creatis/vip/application/server/business/ReproVipBusiness.java new file mode 100644 index 000000000..a59b36bcd --- /dev/null +++ b/vip-application/src/main/java/fr/insalyon/creatis/vip/application/server/business/ReproVipBusiness.java @@ -0,0 +1,257 @@ +package fr.insalyon.creatis.vip.application.server.business; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import fr.insalyon.creatis.vip.application.client.bean.AppVersion; +import fr.insalyon.creatis.vip.application.client.bean.InOutData; +import fr.insalyon.creatis.vip.application.client.bean.Task; +import fr.insalyon.creatis.vip.application.client.view.ApplicationException; +import fr.insalyon.creatis.vip.application.server.dao.h2.SimulationData; +import fr.insalyon.creatis.vip.core.client.bean.Execution; +import fr.insalyon.creatis.vip.core.client.bean.User; +import fr.insalyon.creatis.vip.core.server.business.BusinessException; +import fr.insalyon.creatis.vip.core.server.business.ConfigurationBusiness; +import fr.insalyon.creatis.vip.core.server.business.EmailBusiness; +import fr.insalyon.creatis.vip.core.server.business.Server; +import fr.insalyon.creatis.vip.core.server.dao.DAOException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.io.*; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +@Service +@Transactional +public class ReproVipBusiness { + private final Logger logger = LoggerFactory.getLogger(getClass()); + @Autowired + private ConfigurationBusiness configurationBusiness; + @Autowired + private WorkflowBusiness workflowBusiness; + @Autowired + private ApplicationBusiness applicationBusiness; + @Autowired + private EmailBusiness emailBusiness; + @Autowired + private ExecutionInOutData executionInOutData; + @Autowired + private Server server; + @Autowired + private SimulationBusiness simulationBusiness; + public void executionAdminEmail(Execution execution) throws DAOException, BusinessException { + String adminsEmailContents = "" + + "" + + "" + + "

Dear Administrator,

" + + "

A new user requested to make an execution public

" + + "

Details:

" + + "
    " + + "
  • ID: " + execution.getId() + "
  • " + + "
  • Name: " + execution.getSimulationName() + "
  • " + + "
  • Name: " + execution.getApplicationName() + "
  • " + + "
  • Version: " + execution.getVersion() + "
  • " + + "
  • Status: " + execution.getStatus() + "
  • " + + "
  • Author: " + execution.getAuthor() + "
  • " + + "
  • Comments: " + execution.getComments() + "
  • " + + "
" + + "

Best Regards,

" + + "

VIP Team

" + + "" + + ""; + + logger.info("Sending confirmation email from '" + execution.getAuthor() + "'."); + for (String adminEmail : configurationBusiness.getAdministratorsEmails()) { + emailBusiness.sendEmail("[VIP Admin] Execution Public Request", adminsEmailContents, + new String[]{adminEmail}, true, execution.getAuthor()); + } + logger.info("Email send"); + } + + public ExecutionInOutData executionOutputData(String executionID, User currentUser) throws ApplicationException, BusinessException { + logger.info("Fetching data for executionID: {}", executionID); + + List outputData = workflowBusiness.getOutputData(executionID, currentUser.getFolder()); + logger.info(String.valueOf(outputData)); + List inputData = workflowBusiness.getInputData(executionID, currentUser.getFolder()); + + if (outputData != null) { + logger.info("Fetched {} output data items", outputData.size()); + logger.info(outputData.toString()); + } else { + logger.info("Output data is null for executionID: {}", executionID); + } + + if (inputData != null) { + logger.info("Fetched {} input data items", inputData.size()); + logger.info(inputData.toString()); + } else { + logger.info("Input data is null for executionID: {}", executionID); + } + + return new ExecutionInOutData(inputData, outputData); + } + + public ExecutionJobTaskData getExecutionJobTaskData(String executionID) throws BusinessException { + logger.info("Fetching job and task data for executionID: {}", executionID); + List jobList = simulationBusiness.getJobsList(executionID); + if (jobList == null || jobList.isEmpty()) { + logger.info("No jobs found for executionID: {}", executionID); + } + return new ExecutionJobTaskData(jobList); + } + public String generateReprovipJson(Path reproVipDir, String executionName, String executionID, String version, + String comments, User currentUser, List provenanceFiles) + throws BusinessException, DAOException { + Path workflowVipDir = Paths.get("/vip/ReproVip/" + executionID); + String filesToDownload = getDescritporBoutiqueJsonPath(executionName, version); + + List filesToUpload = provenanceFiles.stream() + .map(Path::toString) + .collect(Collectors.toList()); + + Map structuredJson = new LinkedHashMap<>(); + structuredJson.put("path_workflow_directory", workflowVipDir); + structuredJson.put("descriptor_boutique", filesToDownload); + structuredJson.put("files_to_upload", filesToUpload); + + Map> invocationOutputsMap = new LinkedHashMap<>(); + + for (Path provenanceFile : provenanceFiles) { + String fileName = provenanceFile.getFileName().toString(); + String invocationID = fileName.substring(0, fileName.indexOf(".sh.provenance.json")); + List outputDataPaths = simulationBusiness.getSimulationDAO(executionID).getOutputData(invocationID); + invocationOutputsMap.put(invocationID, outputDataPaths); + } + + structuredJson.put("invocation_outputs", invocationOutputsMap); + + Map metadataOuter = new LinkedHashMap<>(); + Map metadataInner = new LinkedHashMap<>(); + + metadataInner.put("title", "your title"); + metadataInner.put("upload_type", "workflow"); + metadataInner.put("description", comments); + + List> creators = new ArrayList<>(); + Map creator = new LinkedHashMap<>(); + creator.put("name", currentUser.getFullName()); + creator.put("affiliation", "your affiliation"); + creators.add(creator); + + metadataInner.put("creators", creators); + metadataOuter.put("metadata", metadataInner); + + structuredJson.put("metadata", metadataOuter); + + ObjectMapper objectMapper = new ObjectMapper(); + try { + String json = objectMapper.writeValueAsString(structuredJson); + logger.info(json); + + Path reprovipJsonPath = reproVipDir.resolve("workflowMetadata.json"); + saveJsonToFile(json, reprovipJsonPath); + + return json; + } catch (IOException e) { + throw new BusinessException("Failed to save JSON to file", e); + } + } + public void saveJsonToFile(String jsonContent, Path filePath) throws IOException { + Files.writeString(filePath, jsonContent); + } + public String createReproVipDirectory(String executionName, String executionID, String version, String comments, User currentUser) throws BusinessException, DAOException { + Path reproVipDir = Paths.get("/vip/ReproVip/" + executionID); + logger.info("Creating reprovip dir : {}", reproVipDir); + try { + + if ( ! Files.exists(reproVipDir)) { + Files.createDirectories(reproVipDir); + } + } catch (IOException e) { + logger.error("Exception creating the a reprovip directory {}", reproVipDir, e); + throw new RuntimeException(e); + } + List provenanceFiles = copyProvenanceFiles(reproVipDir, executionID); + return generateReprovipJson(reproVipDir, executionName, executionID, version, comments, currentUser, provenanceFiles); + } + + public List copyProvenanceFiles(Path reproVipDir, String executionID) { + try { + logger.debug("Workflows path: " + server.getWorkflowsPath()); + List copiedProvenanceFiles = new ArrayList<>(); + + // directory where provenance files are stored + Path provenanceDirPath = Paths.get(server.getWorkflowsPath() + "/" + executionID + "/provenance"); + if (!Files.exists(provenanceDirPath)) { + logger.error("Provenance directory does not exist: " + provenanceDirPath); + return copiedProvenanceFiles; + } + + try (Stream stream = Files.list(provenanceDirPath)) { + List provenanceFiles = stream + .filter(path -> path.toString().endsWith(".sh.provenance.json")) + .collect(Collectors.toList()); + + for (Path provenanceFile : provenanceFiles) { + // Extract the invocationID from the provenance file name + String fileName = provenanceFile.getFileName().toString(); + // The invocationID is between the first dash and ".sh.provenance.json" + String invocationID = fileName.substring(0, fileName.indexOf(".sh.provenance.json")); + + // Create subfolder named with the invocationID + Path invocationDir = reproVipDir.resolve(invocationID); + if (!Files.exists(invocationDir)) { + Files.createDirectories(invocationDir); + } + + // Copy the source file to the new subfolder + Path newLocation = invocationDir.resolve(provenanceFile.getFileName()); + Files.copy(provenanceFile, newLocation, StandardCopyOption.REPLACE_EXISTING); + logger.info("Copied provenance file to directory: {}", newLocation); + + copiedProvenanceFiles.add(newLocation); + } + } + return copiedProvenanceFiles; + } catch (IOException e) { + logger.error("Error while copying provenance files", e); + throw new RuntimeException(e); + } + } + public String getDescritporBoutiqueJsonPath(String executionName, String version) throws BusinessException { + AppVersion appVersion = applicationBusiness.getVersion(executionName, version); + if (appVersion != null && appVersion.getJsonLfn() != null) { + return appVersion.getJsonLfn(); + } + return null; + } + public void deleteReproVipDirectory(String executionID) throws IOException { + Path reproVipDir = Paths.get("/vip/ReproVip/" + executionID); + logger.info("Deleting ReproVip directory: {}", reproVipDir); + + if (Files.exists(reproVipDir)) { + Files.walk(reproVipDir) + .sorted(Comparator.reverseOrder()) + .forEach(path -> { + try { + Files.delete(path); + logger.info("Deleted: {}", path); + } catch (IOException e) { + logger.error("Error deleting: {}", path, e); + throw new RuntimeException("Failed to delete " + path, e); + } + }); + } else { + logger.info("ReproVip directory does not exist: {}", reproVipDir); + } + } +} diff --git a/vip-application/src/main/java/fr/insalyon/creatis/vip/application/server/business/WorkflowBusiness.java b/vip-application/src/main/java/fr/insalyon/creatis/vip/application/server/business/WorkflowBusiness.java index 9072983ab..f8147f436 100644 --- a/vip-application/src/main/java/fr/insalyon/creatis/vip/application/server/business/WorkflowBusiness.java +++ b/vip-application/src/main/java/fr/insalyon/creatis/vip/application/server/business/WorkflowBusiness.java @@ -447,6 +447,17 @@ public Descriptor getApplicationDescriptor( } } + public String getRawApplicationDescriptorPath(User user, String applicationName, String applicationVersion) + throws BusinessException { + + try { + AppVersion version = applicationDAO.getVersion(applicationName, applicationVersion); + return dataManagerBusiness.getRemoteFile(user, version.getJsonLfn()); + } catch (DAOException | BusinessException ex) { + throw new BusinessException(WRONG_APPLICATION_DESCRIPTOR, ex, applicationName + "/" + applicationVersion); + } + } + public void kill(String simulationID) throws BusinessException { try { @@ -553,25 +564,38 @@ public Simulation getSimulation(String simulationID, boolean refresh) return simulation; } + public List getOutputData( - String simulationID, String currentUserFolder) + String simulationID, String currentUserFolder) throws BusinessException { + return getOutputData(simulationID, currentUserFolder, false); + } + + public List getRawOutputData( + String simulationID) throws BusinessException { + return getOutputData(simulationID, null, true); + } + + private List getOutputData( + String simulationID, String currentUserFolder, boolean useRawPath) throws BusinessException { List list = new ArrayList(); try { for (Output output : outputDAO.get(simulationID)) { - String path = lfcPathsBusiness.parseRealDir( - output.getOutputID().getPath(), currentUserFolder); + String path; + if (useRawPath) { + path = output.getOutputID().getPath(); + } else { + path = lfcPathsBusiness.parseRealDir( + output.getOutputID().getPath(), currentUserFolder); + } list.add(new InOutData(path, output.getOutputID().getProcessor(), output.getType().name())); } - } catch (WorkflowsDBDAOException ex) { + } catch (WorkflowsDBDAOException | DataManagerException ex) { logger.error("Error getting output data for {}", simulationID, ex); throw new BusinessException(ex); - } catch (DataManagerException ex) { - throw new BusinessException(ex); } - return list; } diff --git a/vip-application/src/main/java/fr/insalyon/creatis/vip/application/server/dao/h2/SimulationData.java b/vip-application/src/main/java/fr/insalyon/creatis/vip/application/server/dao/h2/SimulationData.java index 5bde79db2..5d3b655f1 100644 --- a/vip-application/src/main/java/fr/insalyon/creatis/vip/application/server/dao/h2/SimulationData.java +++ b/vip-application/src/main/java/fr/insalyon/creatis/vip/application/server/dao/h2/SimulationData.java @@ -450,6 +450,7 @@ public Map getNodeCountriesMap() throws DAOException { + "GROUP BY country ORDER BY country"); ps.setString(1, TaskStatus.COMPLETED.name()); ResultSet rs = ps.executeQuery(); + Map map = new HashMap(); while (rs.next()) { diff --git a/vip-application/src/main/java/fr/insalyon/creatis/vip/application/server/rpc/ExecutionsServiceImpl.java b/vip-application/src/main/java/fr/insalyon/creatis/vip/application/server/rpc/ExecutionsServiceImpl.java new file mode 100644 index 000000000..03687ab4a --- /dev/null +++ b/vip-application/src/main/java/fr/insalyon/creatis/vip/application/server/rpc/ExecutionsServiceImpl.java @@ -0,0 +1,36 @@ +package fr.insalyon.creatis.vip.application.server.rpc; + +import fr.insalyon.creatis.vip.application.client.rpc.ExecutionsService; +import fr.insalyon.creatis.vip.application.client.view.ApplicationException; +import fr.insalyon.creatis.vip.application.server.business.*; +import fr.insalyon.creatis.vip.core.client.bean.Execution; +import fr.insalyon.creatis.vip.core.server.business.BusinessException; +import fr.insalyon.creatis.vip.core.server.business.ConfigurationBusiness; +import fr.insalyon.creatis.vip.core.server.rpc.AbstractRemoteServiceServlet; +import org.springframework.beans.factory.annotation.Autowired; + +import javax.servlet.ServletException; +import java.util.List; + +public class ExecutionsServiceImpl extends AbstractRemoteServiceServlet implements ExecutionsService { + @Autowired + private ExecutionsBusiness executionsBusiness; + @Override + public void init() throws ServletException { + super.init(); + executionsBusiness = getBean(ExecutionsBusiness.class); + } + @Override + public List getExecutions() throws ApplicationException { + if(executionsBusiness != null) { + try { + return executionsBusiness.getExecutions(); + } catch (BusinessException e) { + throw new ApplicationException(e); + } + } else { + throw new ApplicationException("ExecutionsBusiness est null"); + } + } + +} diff --git a/vip-application/src/main/java/fr/insalyon/creatis/vip/application/server/rpc/JobServiceImpl.java b/vip-application/src/main/java/fr/insalyon/creatis/vip/application/server/rpc/JobServiceImpl.java index 32a709c5a..364f51ea0 100644 --- a/vip-application/src/main/java/fr/insalyon/creatis/vip/application/server/rpc/JobServiceImpl.java +++ b/vip-application/src/main/java/fr/insalyon/creatis/vip/application/server/rpc/JobServiceImpl.java @@ -45,6 +45,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.ApplicationContext; +import org.springframework.stereotype.Service; import org.springframework.web.context.support.WebApplicationContextUtils; import javax.servlet.ServletException; diff --git a/vip-application/src/main/java/fr/insalyon/creatis/vip/application/server/rpc/ReproVipServiceImpl.java b/vip-application/src/main/java/fr/insalyon/creatis/vip/application/server/rpc/ReproVipServiceImpl.java new file mode 100644 index 000000000..2d0d15c78 --- /dev/null +++ b/vip-application/src/main/java/fr/insalyon/creatis/vip/application/server/rpc/ReproVipServiceImpl.java @@ -0,0 +1,76 @@ +package fr.insalyon.creatis.vip.application.server.rpc; + +import fr.insalyon.creatis.vip.application.server.business.ReproVipBusiness; +import fr.insalyon.creatis.vip.core.client.bean.Execution; +import fr.insalyon.creatis.vip.application.client.rpc.ReproVipService; +import fr.insalyon.creatis.vip.core.client.bean.User; +import fr.insalyon.creatis.vip.core.client.view.CoreException; +import fr.insalyon.creatis.vip.core.server.business.BusinessException; +import fr.insalyon.creatis.vip.core.server.dao.DAOException; +import fr.insalyon.creatis.vip.core.server.dao.ExecutionPublicDAO; +import fr.insalyon.creatis.vip.core.server.rpc.AbstractRemoteServiceServlet; + +import javax.servlet.ServletException; +import java.io.IOException; + +public class ReproVipServiceImpl extends AbstractRemoteServiceServlet implements ReproVipService { + private ReproVipBusiness reproVipBusiness; + private ExecutionPublicDAO executionPublicDAO; + @Override + public void init() throws ServletException { + super.init(); + reproVipBusiness = getBean(ReproVipBusiness.class); + executionPublicDAO = getBean(ExecutionPublicDAO.class); + } + + public void executionAdminEmail(Execution execution) { + try { + reproVipBusiness.executionAdminEmail(execution); + } catch (DAOException e) { + throw new RuntimeException(e); + } catch (BusinessException e) { + throw new RuntimeException(e); + } + } + + // execution management + @Override + public void addExecution(Execution execution) throws CoreException { + try { + executionPublicDAO.add(execution); + } catch (DAOException e) { + throw new CoreException("Failed to add execution", e); + } + } + @Override + public void updateExecution(String executionID, String newStatus) throws CoreException { + try { + executionPublicDAO.update(executionID, newStatus); + } catch (DAOException e) { + throw new CoreException("Failed to update execution", e); + } + } + @Override + public boolean doesExecutionExist(String executionID) throws CoreException { + try { + return executionPublicDAO.doesExecutionExist(executionID); + } catch (DAOException e) { + throw new CoreException("Failed to check if execution exist", e); + } + } + public String createReproVipDirectory(String executionName, String executionID, String version, String comments) { + try { + User currentUser = getSessionUser(); + return reproVipBusiness.createReproVipDirectory(executionName, executionID, version, comments, currentUser); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + public void deleteReproVipDirectory(String executionID) throws CoreException { + try { + reproVipBusiness.deleteReproVipDirectory(executionID); + } catch (IOException e) { + throw new RuntimeException(e); + } + } +} diff --git a/vip-application/src/main/java/fr/insalyon/creatis/vip/application/server/rpc/WorkflowServiceImpl.java b/vip-application/src/main/java/fr/insalyon/creatis/vip/application/server/rpc/WorkflowServiceImpl.java index b2631fe62..d11b4a43b 100644 --- a/vip-application/src/main/java/fr/insalyon/creatis/vip/application/server/rpc/WorkflowServiceImpl.java +++ b/vip-application/src/main/java/fr/insalyon/creatis/vip/application/server/rpc/WorkflowServiceImpl.java @@ -51,6 +51,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.ApplicationContext; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import org.springframework.web.context.support.WebApplicationContextUtils; import javax.servlet.ServletException; @@ -67,6 +69,8 @@ * * @author Rafael Ferreira da Silva */ +@Service +@Transactional public class WorkflowServiceImpl extends AbstractRemoteServiceServlet implements WorkflowService { private final Logger logger = LoggerFactory.getLogger(getClass()); @@ -147,6 +151,15 @@ public Descriptor getApplicationDescriptor(String applicationName, String applic } } + @Override + public String getRawApplicationDescriptorPath(String applicationName, String applicationVersion) throws ApplicationException { + try { + return workflowBusiness.getRawApplicationDescriptorPath(getSessionUser(), applicationName, applicationVersion); + } catch (BusinessException | CoreException ex) { + throw new ApplicationException(ex); + } + } + /** * * @param applicationName @@ -679,6 +692,7 @@ public List getOutputData(String simulationID) throws ApplicationExce } } + /** * * @param simulationID diff --git a/vip-core/pom.xml b/vip-core/pom.xml index 10c510f9a..6fd24d8ed 100644 --- a/vip-core/pom.xml +++ b/vip-core/pom.xml @@ -141,6 +141,12 @@ knowledge of the CeCILL-B license and that you accept its terms. 1.3.2 + + com.fasterxml.jackson.core + jackson-databind + ${jackson.version} + + org.springframework @@ -157,7 +163,7 @@ knowledge of the CeCILL-B license and that you accept its terms. spring-context ${spring-framework.version} - + diff --git a/vip-core/src/main/java/fr/insalyon/creatis/vip/core/client/bean/Execution.java b/vip-core/src/main/java/fr/insalyon/creatis/vip/core/client/bean/Execution.java new file mode 100644 index 000000000..3ffaeb6d1 --- /dev/null +++ b/vip-core/src/main/java/fr/insalyon/creatis/vip/core/client/bean/Execution.java @@ -0,0 +1,43 @@ +package fr.insalyon.creatis.vip.core.client.bean; + +import com.google.gwt.user.client.rpc.IsSerializable; + +public class Execution implements IsSerializable { + private String id; + private String simulationName; + private String applicationName; + private String version; + private String status; + private String author; + private String comments; + public Execution() {} + + public Execution(String id, String simulationName,String applicationName, String version, String status, String author, String comments) { + this.id = id; + this.simulationName = simulationName; + this.applicationName = applicationName; + this.version = version; + this.status = status; + this.author = author; + this.comments = comments; + } + + public String getId() { return id; } + public String getSimulationName() { + return simulationName; + } + public String getApplicationName() { return applicationName;} + public String getVersion() { + return version; + } + public String getStatus() { + return status; + } + public String getAuthor() { + return author; + } + public String getComments() { + return comments; + } +} + diff --git a/vip-core/src/main/java/fr/insalyon/creatis/vip/core/client/rpc/ConfigurationService.java b/vip-core/src/main/java/fr/insalyon/creatis/vip/core/client/rpc/ConfigurationService.java index 8486dcaf5..660b58ce8 100644 --- a/vip-core/src/main/java/fr/insalyon/creatis/vip/core/client/rpc/ConfigurationService.java +++ b/vip-core/src/main/java/fr/insalyon/creatis/vip/core/client/rpc/ConfigurationService.java @@ -34,10 +34,7 @@ import com.google.gwt.core.client.GWT; import com.google.gwt.user.client.rpc.RemoteService; import com.google.gwt.user.client.rpc.ServiceDefTarget; -import fr.insalyon.creatis.vip.core.client.bean.Account; -import fr.insalyon.creatis.vip.core.client.bean.Group; -import fr.insalyon.creatis.vip.core.client.bean.UsageStats; -import fr.insalyon.creatis.vip.core.client.bean.User; +import fr.insalyon.creatis.vip.core.client.bean.*; import fr.insalyon.creatis.vip.core.client.view.CoreConstants; import fr.insalyon.creatis.vip.core.client.view.CoreException; import fr.insalyon.creatis.vip.core.client.view.user.UserLevel; diff --git a/vip-core/src/main/java/fr/insalyon/creatis/vip/core/client/rpc/ConfigurationServiceAsync.java b/vip-core/src/main/java/fr/insalyon/creatis/vip/core/client/rpc/ConfigurationServiceAsync.java index c213e1fcf..a50300510 100644 --- a/vip-core/src/main/java/fr/insalyon/creatis/vip/core/client/rpc/ConfigurationServiceAsync.java +++ b/vip-core/src/main/java/fr/insalyon/creatis/vip/core/client/rpc/ConfigurationServiceAsync.java @@ -32,10 +32,7 @@ package fr.insalyon.creatis.vip.core.client.rpc; import com.google.gwt.user.client.rpc.AsyncCallback; -import fr.insalyon.creatis.vip.core.client.bean.Account; -import fr.insalyon.creatis.vip.core.client.bean.Group; -import fr.insalyon.creatis.vip.core.client.bean.UsageStats; -import fr.insalyon.creatis.vip.core.client.bean.User; +import fr.insalyon.creatis.vip.core.client.bean.*; import fr.insalyon.creatis.vip.core.client.view.CoreConstants; import fr.insalyon.creatis.vip.core.client.view.user.UserLevel; import fr.insalyon.creatis.vip.core.client.view.util.CountryCode; diff --git a/vip-core/src/main/java/fr/insalyon/creatis/vip/core/client/view/CoreConstants.java b/vip-core/src/main/java/fr/insalyon/creatis/vip/core/client/view/CoreConstants.java index dd3dd1c84..cc52f87f2 100644 --- a/vip-core/src/main/java/fr/insalyon/creatis/vip/core/client/view/CoreConstants.java +++ b/vip-core/src/main/java/fr/insalyon/creatis/vip/core/client/view/CoreConstants.java @@ -98,6 +98,7 @@ public class CoreConstants implements IsSerializable { public static final String TAB_MANAGE_TIPS = "manage_tips_tab"; public static final String TAB_MANAGE_USERS = "manage_users_tab"; public static final String TAB_MANAGE_SETTING = "manage_setting_tab"; + public static final String TAB_MAKE_EXECUTION_PUBLIC = "make_execution_public_tab"; // home actions public static final String HOME_ACTION_SHOW_APPLICATIONS = "SHOW_APPLICATIONS"; public static final String HOME_ACTION_SHOW_PUBLICATIONS = "SHOW_PUBLICATIONS"; diff --git a/vip-core/src/main/java/fr/insalyon/creatis/vip/core/server/business/ConfigurationBusiness.java b/vip-core/src/main/java/fr/insalyon/creatis/vip/core/server/business/ConfigurationBusiness.java index a8c01f903..d33423cb4 100644 --- a/vip-core/src/main/java/fr/insalyon/creatis/vip/core/server/business/ConfigurationBusiness.java +++ b/vip-core/src/main/java/fr/insalyon/creatis/vip/core/server/business/ConfigurationBusiness.java @@ -938,7 +938,7 @@ public void resetPassword(String email, String code, String password) /** * Gets an array of administrator's e-mails */ - private String[] getAdministratorsEmails() throws DAOException { + public String[] getAdministratorsEmails() throws DAOException { List emails = new ArrayList<>(); for (User admin : userDAO.getAdministrators()) { emails.add(admin.getEmail()); diff --git a/vip-core/src/main/java/fr/insalyon/creatis/vip/core/server/dao/ExecutionPublicDAO.java b/vip-core/src/main/java/fr/insalyon/creatis/vip/core/server/dao/ExecutionPublicDAO.java new file mode 100644 index 000000000..8f6cdeca7 --- /dev/null +++ b/vip-core/src/main/java/fr/insalyon/creatis/vip/core/server/dao/ExecutionPublicDAO.java @@ -0,0 +1,13 @@ +package fr.insalyon.creatis.vip.core.server.dao; + +import fr.insalyon.creatis.vip.core.client.bean.Execution; + +import java.util.List; + +public interface ExecutionPublicDAO { + public void add(Execution execution) throws DAOException; + void update(String executionId, String newStatus) throws DAOException; + List getExecutions() throws DAOException; + public boolean doesExecutionExist(String executionId) throws DAOException; +} + diff --git a/vip-core/src/main/java/fr/insalyon/creatis/vip/core/server/dao/mysql/CoreDataInitializer.java b/vip-core/src/main/java/fr/insalyon/creatis/vip/core/server/dao/mysql/CoreDataInitializer.java index 0e91474b6..94f54735d 100644 --- a/vip-core/src/main/java/fr/insalyon/creatis/vip/core/server/dao/mysql/CoreDataInitializer.java +++ b/vip-core/src/main/java/fr/insalyon/creatis/vip/core/server/dao/mysql/CoreDataInitializer.java @@ -61,6 +61,7 @@ public void onStartup() { initializeUserTables(); initializeGroupTables(); initializeTermsOfUseTable(); + initializeExecutionTables(); } private void initializeUserTables() { @@ -160,4 +161,26 @@ private void initializeTermsOfUseTable() { } } } + + private void initializeExecutionTables() { + if (tableInitializer.createTable("VIPExecutionPublic", + "execution_ID VARCHAR(255), " + + "simulation_name VARCHAR(255), " + + "application_name VARCHAR(255), " + + "version VARCHAR(255), " + + "status VARCHAR(255), " + + "author VARCHAR(255), " + + "comments TEXT, " + + "PRIMARY KEY(execution_ID)")) { + + try { + usersGroupsDAO.add(server.getAdminEmail(), + CoreConstants.GROUP_SUPPORT, + GROUP_ROLE.Admin); + } catch (DAOException ex) { + logger.error("Error creating ExecutionPublic table", ex); + } + } + } + } diff --git a/vip-core/src/main/java/fr/insalyon/creatis/vip/core/server/dao/mysql/ExecutionDataPublic.java b/vip-core/src/main/java/fr/insalyon/creatis/vip/core/server/dao/mysql/ExecutionDataPublic.java new file mode 100644 index 000000000..8ae1d1319 --- /dev/null +++ b/vip-core/src/main/java/fr/insalyon/creatis/vip/core/server/dao/mysql/ExecutionDataPublic.java @@ -0,0 +1,107 @@ +package fr.insalyon.creatis.vip.core.server.dao.mysql; + +import fr.insalyon.creatis.vip.core.client.bean.Execution; +import fr.insalyon.creatis.vip.core.server.dao.DAOException; +import fr.insalyon.creatis.vip.core.server.dao.ExecutionPublicDAO; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.jdbc.core.support.JdbcDaoSupport; +import org.springframework.stereotype.Repository; +import org.springframework.transaction.annotation.Transactional; + +import javax.sql.DataSource; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; + +@Repository +@Transactional +public class ExecutionDataPublic extends JdbcDaoSupport implements ExecutionPublicDAO { + private final Logger logger = LoggerFactory.getLogger(getClass()); + @Autowired + public void useDataSource(DataSource dataSource) { + setDataSource(dataSource); + } + @Override + public void add(Execution execution) throws DAOException { + try { + PreparedStatement ps = getConnection().prepareStatement( + "INSERT INTO VIPExecutionPublic(execution_ID, simulation_name, application_name, version, status, author, comments) VALUES(?, ?, ?, ?, ?, ?, ?)"); + ps.setString(1, execution.getId()); + ps.setString(2, execution.getSimulationName()); + ps.setString(3, execution.getApplicationName()); + ps.setString(4, execution.getVersion()); + ps.setString(5, execution.getStatus()); + ps.setString(6, execution.getAuthor()); + ps.setString(7, execution.getComments()); + ps.execute(); + ps.close(); + + } catch (SQLException ex) { + if (ex.getMessage().contains("Duplicate entry")) { + throw new DAOException("Error creating an execution", ex); + } else { + throw new DAOException(ex); + } + } + } + + @Override + public void update(String executionId, String newStatus) throws DAOException { + try (PreparedStatement ps = getConnection().prepareStatement( + "UPDATE VIPExecutionPublic SET status = ? WHERE execution_ID = ?")) { + ps.setString(1, newStatus); + ps.setString(2, executionId); + ps.execute(); + + } catch (SQLException ex) { + throw new DAOException("Error updating execution status", ex); + } + } + + @Override + public List getExecutions() throws DAOException { + try { + List executions = new ArrayList<>(); + PreparedStatement ps = getConnection().prepareStatement("SELECT * FROM VIPExecutionPublic"); + ResultSet rs = ps.executeQuery(); + while (rs.next()) { + executions.add(new Execution( + rs.getString("execution_ID"), + rs.getString("simulation_name"), + rs.getString("application_name"), + rs.getString("version"), + rs.getString("status"), + rs.getString("author"), + rs.getString("comments") + )); + logger.info(executions.toString()); + } + rs.close(); + ps.close(); + return executions; + } catch (SQLException ex) { + throw new DAOException("Error getting executions", ex); + } + } + + @Override + public boolean doesExecutionExist(String executionId) throws DAOException { + try (PreparedStatement ps = getConnection().prepareStatement( + "SELECT COUNT(*) FROM VIPExecutionPublic WHERE execution_ID = ?")) { + ps.setString(1, executionId); + try (ResultSet rs = ps.executeQuery()) { + if (rs.next()) { + return rs.getInt(1) > 0; + } + return false; + } + } catch (SQLException ex) { + throw new DAOException("Error checking if execution exists", ex); + } + } +} + diff --git a/vip-core/src/main/java/fr/insalyon/creatis/vip/core/server/rpc/AbstractRemoteServiceServlet.java b/vip-core/src/main/java/fr/insalyon/creatis/vip/core/server/rpc/AbstractRemoteServiceServlet.java index 71cab4952..1e3e13ac2 100644 --- a/vip-core/src/main/java/fr/insalyon/creatis/vip/core/server/rpc/AbstractRemoteServiceServlet.java +++ b/vip-core/src/main/java/fr/insalyon/creatis/vip/core/server/rpc/AbstractRemoteServiceServlet.java @@ -42,6 +42,8 @@ import org.slf4j.LoggerFactory; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import org.springframework.web.context.support.WebApplicationContextUtils; import javax.servlet.ServletException; diff --git a/vip-core/src/main/java/fr/insalyon/creatis/vip/core/server/rpc/ConfigurationServiceImpl.java b/vip-core/src/main/java/fr/insalyon/creatis/vip/core/server/rpc/ConfigurationServiceImpl.java index a4c1a6cbe..63e3b1dac 100644 --- a/vip-core/src/main/java/fr/insalyon/creatis/vip/core/server/rpc/ConfigurationServiceImpl.java +++ b/vip-core/src/main/java/fr/insalyon/creatis/vip/core/server/rpc/ConfigurationServiceImpl.java @@ -32,10 +32,7 @@ package fr.insalyon.creatis.vip.core.server.rpc; import fr.insalyon.creatis.grida.client.GRIDAClient; -import fr.insalyon.creatis.vip.core.client.bean.Account; -import fr.insalyon.creatis.vip.core.client.bean.Group; -import fr.insalyon.creatis.vip.core.client.bean.UsageStats; -import fr.insalyon.creatis.vip.core.client.bean.User; +import fr.insalyon.creatis.vip.core.client.bean.*; import fr.insalyon.creatis.vip.core.client.rpc.ConfigurationService; import fr.insalyon.creatis.vip.core.client.view.CoreConstants; import fr.insalyon.creatis.vip.core.client.view.CoreConstants.GROUP_ROLE; @@ -45,7 +42,9 @@ import fr.insalyon.creatis.vip.core.server.business.BusinessException; import fr.insalyon.creatis.vip.core.server.business.ConfigurationBusiness; import fr.insalyon.creatis.vip.core.server.dao.DAOException; +import fr.insalyon.creatis.vip.core.server.dao.ExecutionPublicDAO; import fr.insalyon.creatis.vip.core.server.dao.UserDAO; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -616,8 +615,6 @@ public void updateLastUpdatePublication() throws CoreException { } // api key management - - @Override public String getUserApikey(String email) throws CoreException { try { @@ -647,4 +644,5 @@ public String generateNewUserApikey(String email) throws CoreException { throw new CoreException(ex); } } + } diff --git a/vip-portal/src/main/webapp/WEB-INF/web.xml b/vip-portal/src/main/webapp/WEB-INF/web.xml index d1f6f9f9f..2b5648241 100644 --- a/vip-portal/src/main/webapp/WEB-INF/web.xml +++ b/vip-portal/src/main/webapp/WEB-INF/web.xml @@ -94,6 +94,14 @@ knowledge of the CeCILL-B license and that you accept its terms. ApplicationService /fr.insalyon.creatis.vip.portal.Main/applicationservice + + ExecutionsService + fr.insalyon.creatis.vip.application.server.rpc.ExecutionsServiceImpl + + + ExecutionsService + /fr.insalyon.creatis.vip.portal.Main/executionsservice + WorkflowService fr.insalyon.creatis.vip.application.server.rpc.WorkflowServiceImpl @@ -202,6 +210,16 @@ knowledge of the CeCILL-B license and that you accept its terms. index.html + + + ReproVipService + fr.insalyon.creatis.vip.application.server.rpc.ReproVipServiceImpl + + + ReproVipService + /fr.insalyon.creatis.vip.portal.Main/reprovipservice + +