Skip to content
Snippets Groups Projects
Commit 877e8da4 authored by BIGARET Sebastien's avatar BIGARET Sebastien
Browse files

Merge branch 'release/1.9'

parents c8b15937 a715e726
No related branches found
No related tags found
No related merge requests found
Pipeline #87 failed
v1.9
----
- By default, client.StorageManager now ignores update requests for unknown
results. A delegate can be provided to examine each of those requests and
decide whether they should be accepted --for details, see
StorageManager.StorageManagerDelegateInterface.
This also fixes the situation where "ghost" execution-in-progress items
where coming back each time the application was relaunched, no matter how
many time you delete them (these items are processes in the RUNNING state
whose processes is not executed anymore but which remain in the server
database after a crash e.g.).
- Enhanced logging:
- the number of connected users is now logged when a user successfully
connects (it was only logged when a user disconnects).
- the list of programs'ids in every executed workflow
v1.8.1 v1.8.1
------ ------
......
Copyright Institut Télécom-Télécom Bretagne, 2002-2012 Copyright Institut Télécom-Télécom Bretagne, 2002-2013
Contributeurs: Project leader, maintenance:
Sébastien Bigaret <sebastien.bigaret@telecom-bretagne.eu> Sébastien Bigaret <sebastien.bigaret@telecom-bretagne.eu>
Philippe Picouet <philippe.picouet@telecom-bretagne.eu>
Ce logiciel, Praxis, est un environnement logiciel de fédération et de Ce logiciel, Praxis, est un environnement logiciel de fédération et de
combinaison de services distribués au sein de workflows. combinaison de services distribués au sein de workflows.
......
Copyright Institut Télécom-Télécom Bretagne, 2002-2012 Copyright Institut Télécom-Télécom Bretagne, 2002-2013
Contributors: Project leader, maintainer:
Sébastien Bigaret <sebastien.bigaret@telecom-bretagne.eu> Sébastien Bigaret <sebastien.bigaret@telecom-bretagne.eu>
Philippe Picouet <philippe.picouet@telecom-bretagne.eu>
This software, Praxis, is a software environment for federating and This software, Praxis, is a software environment for federating and
combining distributed services using workflows. combining distributed services using workflows.
......
...@@ -16,6 +16,10 @@ UI.actions.finished_exec_with_warnings.msg = The execution of {0} is ...@@ -16,6 +16,10 @@ UI.actions.finished_exec_with_warnings.msg = The execution of {0} is
UI.actions.finished_exec.title = End of execution UI.actions.finished_exec.title = End of execution
UI.actions.wf_close = Close UI.actions.wf_close = Close
UI.actions.wf_close_tt = Close current workflow UI.actions.wf_close_tt = Close current workflow
UI.actions.wf_close_all = Close all
UI.actions.wf_close_all_tt = Close all workflows
UI.actions.wf_close_others = Close others
UI.actions.wf_close_others_tt = Close other workflows
UI.actions.wf_export = Export without input files... UI.actions.wf_export = Export without input files...
UI.actions.wf_export_tt = Export a workflow UI.actions.wf_export_tt = Export a workflow
UI.actions.wf_export_with_infiles = Export with input files... UI.actions.wf_export_with_infiles = Export with input files...
...@@ -64,7 +68,7 @@ UI.dialog.about_content = <html>\ ...@@ -64,7 +68,7 @@ UI.dialog.about_content = <html>\
{3}\ {3}\
<hr>\ <hr>\
<i>Powered by Praxis - Version {0} - date: {2}<br>\ <i>Powered by Praxis - Version {0} - date: {2}<br>\
Copyright Institut Telecom-Telecom Bretagne, 2002-2012<br>\ Copyright Institut Telecom-Telecom Bretagne, 2002-2013<br>\
All rights reserved</i>\ All rights reserved</i>\
</html> </html>
UI.dialog.about_msg = UI.dialog.about_msg =
...@@ -174,7 +178,7 @@ UI.dialog.wf.open_failure = An unexpected error occu ...@@ -174,7 +178,7 @@ UI.dialog.wf.open_failure = An unexpected error occu
UI.dialog.wf.rename_save_before_confirm = The workflow has been modified and needs to be saved before it is renamed.\n\ UI.dialog.wf.rename_save_before_confirm = The workflow has been modified and needs to be saved before it is renamed.\n\
Do you want to save it now? Do you want to save it now?
UI.dialog.wf.open_failure_title = Failed to open a workflow UI.dialog.wf.open_failure_title = Failed to open a workflow
UI.dialog.wf.open_select_workflows = Select the workflow(s) to be opene: UI.dialog.wf.open_select_workflows = Select the workflow(s) to be opened:
UI.dialog.wf.rename_enter_new_name = Please enter the new name of this workflow: UI.dialog.wf.rename_enter_new_name = Please enter the new name of this workflow:
UI.dialog.wf.rename_failure_title = Failed to rename a workflow UI.dialog.wf.rename_failure_title = Failed to rename a workflow
UI.dialog.wf.rename_impossible = Failed to rename the workflow (unknown reason).\n\ UI.dialog.wf.rename_impossible = Failed to rename the workflow (unknown reason).\n\
......
...@@ -16,6 +16,10 @@ UI.actions.finished_exec_with_warnings.msg = L''ex\u00E9cution du wor ...@@ -16,6 +16,10 @@ UI.actions.finished_exec_with_warnings.msg = L''ex\u00E9cution du wor
UI.actions.finished_exec.title = Fin de l''ex\u00E9cution d''un workflow UI.actions.finished_exec.title = Fin de l''ex\u00E9cution d''un workflow
UI.actions.wf_close = Fermer UI.actions.wf_close = Fermer
UI.actions.wf_close_tt = Fermer le workflow courant UI.actions.wf_close_tt = Fermer le workflow courant
UI.actions.wf_close_all = Tout fermer
UI.actions.wf_close_all_tt = Fermer tous les workflows
UI.actions.wf_close_others = Fermer les autres
UI.actions.wf_close_others_tt = Fermer les autres workflows
UI.actions.wf_export = Exporter sans les fichiers d''entr\u00E9es...... UI.actions.wf_export = Exporter sans les fichiers d''entr\u00E9es......
UI.actions.wf_export_tt = Exporter un workflow UI.actions.wf_export_tt = Exporter un workflow
UI.actions.wf_export_with_infiles = Exporter avec les fichiers d''entr\u00E9es... UI.actions.wf_export_with_infiles = Exporter avec les fichiers d''entr\u00E9es...
...@@ -64,7 +68,7 @@ UI.dialog.about_content = <html>\ ...@@ -64,7 +68,7 @@ UI.dialog.about_content = <html>\
{3}\ {3}\
<hr>\ <hr>\
<i>Elabor\u00e9 avec Praxis - Version {0} - dat\u00E9 du {2}<br>\ <i>Elabor\u00e9 avec Praxis - Version {0} - dat\u00E9 du {2}<br>\
Copyright Institut T\u00E9l\u00E9com-T\u00E9l\u00E9com Bretagne, 2002-2012<br>\ Copyright Institut T\u00E9l\u00E9com-T\u00E9l\u00E9com Bretagne, 2002-2013<br>\
Tous droits r\u00E9serv\u00E9s</i>\ Tous droits r\u00E9serv\u00E9s</i>\
</html> </html>
UI.dialog.about_msg = UI.dialog.about_msg =
......
...@@ -131,6 +131,40 @@ public class StorageManager ...@@ -131,6 +131,40 @@ public class StorageManager
} }
} }
/**
* Interface for the {@link StorageManager}'s delegate.
* @author Sébastien Bigaret
*/
static public interface StorageManagerDelegateInterface
{
/**
* Called by the storage manager when it is receives update requests concerning unknown results, i.e. results
* whose worklow ID and execution ID do not {@link StorageManager#getResult(WorkflowID, ExecutionID) correspond}
* to any result it holds.
*
* @param result
* @return {@code true} if the result should be accepted by the storage manager.
*/
public boolean acceptUnknownResult(Result result);
}
/**
* The default delegate when none is supplied, or when the delegate is reset (set to {@code null}).
* @author Sébastien Bigaret
*/
static public class DefaultStorageManagerDelegate implements StorageManagerDelegateInterface
{
/**
* Rejects all requests
*
* @return {@code false}
*/
public boolean acceptUnknownResult(Result result)
{
return false;
}
}
static public final String SERIALIZED_RESULT_FILENAME = "praxis_serialized_result.xml"; static public final String SERIALIZED_RESULT_FILENAME = "praxis_serialized_result.xml";
/** Paths of workflows */ /** Paths of workflows */
...@@ -158,6 +192,7 @@ public class StorageManager ...@@ -158,6 +192,7 @@ public class StorageManager
* */ * */
static public boolean storeReceivedExecutionResults = true; static public boolean storeReceivedExecutionResults = true;
static private StorageManagerDelegateInterface delegate = new DefaultStorageManagerDelegate();
static synchronized private void init() static synchronized private void init()
{ {
...@@ -238,6 +273,13 @@ public class StorageManager ...@@ -238,6 +273,13 @@ public class StorageManager
return results_DO_NOT_USE_DIRECTLY_SEE_IMPLEMENTATION_COMMENT_ON_TOP; return results_DO_NOT_USE_DIRECTLY_SEE_IMPLEMENTATION_COMMENT_ON_TOP;
} }
public static void setDelegate(StorageManagerDelegateInterface delegate)
{
if (delegate==null)
delegate=new DefaultStorageManagerDelegate();
StorageManager.delegate = delegate;
}
/** /**
* @param dir * @param dir
* @return * @return
...@@ -318,7 +360,7 @@ public class StorageManager ...@@ -318,7 +360,7 @@ public class StorageManager
catch (InvalidXMLException e) // incl. FileNotFoundExc catch (InvalidXMLException e) // incl. FileNotFoundExc
{ {
Log.log.warning("Invalid workflow in directory: " + dir.getName() + ": " Log.log.warning("Invalid workflow in directory: " + dir.getName() + ": "
+ (e.getMessage() != null ? e.getMessage() : "<unknown reason>\n")); + (e.getMessage() != null ? e.getMessage() : "<unknown reason>"));
} }
return null; return null;
} }
...@@ -636,6 +678,22 @@ public class StorageManager ...@@ -636,6 +678,22 @@ public class StorageManager
return result; return result;
} }
/**
* Asks the delegate whether an unknown result should be accepted. A result is considered as unknown if
* {@link #getResult(WorkflowID, ExecutionID)} returns {@code null} for its execution- and workflow-ids.
* When the result is already known by the storage manager, this methods return {@code true}.
*
* @param result
* the result to test
* @return {@code true} if the result is accepted.
*/
protected static boolean acceptResult(Result result)
{
if (getResult(result.workflowID(), result.executionID()) == null)
return delegate.acceptUnknownResult(result);
return true;
}
/** /**
* Stores the supplied result in the persistent store. * Stores the supplied result in the persistent store.
* @param result the stored result * @param result the stored result
...@@ -855,6 +913,11 @@ public class StorageManager ...@@ -855,6 +913,11 @@ public class StorageManager
Log.log.severe("Received a null result!?!"); Log.log.severe("Received a null result!?!");
return; return;
} }
if (!acceptResult(result))
{
Log.log.info("Rejecting unknown result "+result);
return;
}
try try
{ {
updateResult(result, event.file_conveyor); updateResult(result, event.file_conveyor);
...@@ -879,6 +942,11 @@ public class StorageManager ...@@ -879,6 +942,11 @@ public class StorageManager
for (Result result: results) for (Result result: results)
{ {
if (!acceptResult(result))
{
Log.log.info("Rejecting unknown result "+result);
continue;
}
switch (result.getStatus()) switch (result.getStatus())
{ {
case CANCELLED: case CANCELLED:
...@@ -918,6 +986,11 @@ public class StorageManager ...@@ -918,6 +986,11 @@ public class StorageManager
Log.log.severe("Received a null result!?!"); Log.log.severe("Received a null result!?!");
return; return;
} }
if (!acceptResult(result))
{
Log.log.info("Rejecting unknown result "+result);
return;
}
try try
{ {
updateResult(result, event.file_conveyor); updateResult(result, event.file_conveyor);
......
...@@ -7,15 +7,15 @@ package eu.telecom_bretagne.praxis.common; ...@@ -7,15 +7,15 @@ package eu.telecom_bretagne.praxis.common;
*/ */
public abstract class ReleaseInfo public abstract class ReleaseInfo
{ {
public static final String release = "1.8.1"; public static final String release = "1.9";
public static final int revision = 0; public static final int revision = 0;
public static final int application_revision = Configuration.getInt("revision"); public static final int application_revision = Configuration.getInt("revision");
public static final String release_date = "2012-11-23"; public static final String release_date = "2013-02-19";
public static final String package_date = "2012-11-23"; public static final String package_date = "2013-02-19";
/** /**
* Returns a array of three strings: the release, the application_revision number, and the release date * Returns a array of three strings: the release, the application_revision number, and the release date
......
...@@ -59,7 +59,7 @@ public class RemoteComponents ...@@ -59,7 +59,7 @@ public class RemoteComponents
* should be set to false as soon as possible, ideally at application startup.<br> * should be set to false as soon as possible, ideally at application startup.<br>
* Setting this parameter does not reset the existing cache, if it exists; as a consequence, setting it to false * Setting this parameter does not reset the existing cache, if it exists; as a consequence, setting it to false
* then reverting its value to true makes the component re-use any value previously cached.<br> * then reverting its value to true makes the component re-use any value previously cached.<br>
* Callers should be careful when disabling the cached. It should not be disabled in a normal, standard praxis * Callers should be careful when disabling the cache. It should not be disabled in a normal, standard praxis
* project, because many elements expects that the description returned by * project, because many elements expects that the description returned by
* {@link eu.telecom_bretagne.praxis.core.workflow.Program#getDescription() Program.getDescription()} is always the same objects. However, * {@link eu.telecom_bretagne.praxis.core.workflow.Program#getDescription() Program.getDescription()} is always the same objects. However,
* there exists special contexts when this is needed, especially for applications working on the descriptions * there exists special contexts when this is needed, especially for applications working on the descriptions
......
/* License: please refer to the file README.license located at the root directory of the project */ /* License: please refer to the file README.license located at the root directory of the project */
package eu.telecom_bretagne.praxis.common; package eu.telecom_bretagne.praxis.common;
import java.awt.Desktop;
import java.io.BufferedInputStream; import java.io.BufferedInputStream;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
...@@ -17,6 +18,7 @@ import java.io.ObjectOutputStream; ...@@ -17,6 +18,7 @@ import java.io.ObjectOutputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.Reader; import java.io.Reader;
import java.io.StringWriter; import java.io.StringWriter;
import java.net.URI;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Comparator; import java.util.Comparator;
...@@ -934,6 +936,79 @@ public class Utile ...@@ -934,6 +936,79 @@ public class Utile
return null; return null;
} }
/**
* Tests whether the method {@link #browse(URI)} is supported on the current platform.
* @return {@code true} if the method {@link #browse(URI)} is supported
*/
public static boolean isBrowseSupported()
{
if (!org.apache.commons.lang.SystemUtils.IS_OS_LINUX)
return Desktop.isDesktopSupported() && Desktop.getDesktop().isSupported(Desktop.Action.BROWSE);
File url_open = searchSystemPath("xdg-open");
if (url_open==null)
url_open = searchSystemPath("gnome-open");
return ( (url_open != null && url_open.canExecute())
|| Desktop.isDesktopSupported() && Desktop.getDesktop().isSupported(Desktop.Action.BROWSE) );
}
/**
* Opens an URI in the default browser. This method basically calls {@link Desktop#browse(URI)}, except on Linux
* where it tries to open it with {@code xdg-open} and {@code gnome-open}. Reason is that on Linux, Desktop.browse()
* uses a different browser when the URI points to a local file (URI starting with {@code file://}), while
* {@code xdg-open} and {@code gnome-open} opens such URI with the default web browser (the same as the one
* used when the URI starts with {@code http://} or {@code https://}).
*
* @param uri
* the URI to open in the default browser
* @return {@code true} if the URI could be opened
* @see #isBrowseSupported()
*/
public static boolean browse(URI uri)
{
if (!org.apache.commons.lang.SystemUtils.IS_OS_LINUX)
{
try
{
java.awt.Desktop.getDesktop().browse(uri);
}
catch (IOException e)
{
return false;
}
return true;
}
// Linux
/*
* Desktop.browse() calls gnome_open_url() which behaves differently for http:// or file://, while
* xdg-open does not (and neither does gnome-open).
*/
File url_open = searchSystemPath("xdg-open");
if (url_open==null)
url_open = searchSystemPath("gnome-open");
if (url_open==null)
{
try
{
java.awt.Desktop.getDesktop().browse(uri);
}
catch (IOException e)
{
return false;
}
return true;
}
try
{
Runtime.getRuntime().exec(new String[]{url_open.toString(),uri.toString()});
}
catch (IOException e)
{
e.printStackTrace();
return false;
}
return true;
}
/** /**
* Determines whether a program is registered within the Windows registry, and returns a array suitable for * Determines whether a program is registered within the Windows registry, and returns a array suitable for
* executing it by creating a {@link ProcessBuilder}. The method searches if the program is declared in the * executing it by creating a {@link ProcessBuilder}. The method searches if the program is declared in the
......
...@@ -5,7 +5,7 @@ import java.io.ByteArrayInputStream; ...@@ -5,7 +5,7 @@ import java.io.ByteArrayInputStream;
import java.util.ArrayList; import java.util.ArrayList;
import eu.telecom_bretagne.praxis.common.InvalidXMLException; import eu.telecom_bretagne.praxis.common.InvalidXMLException;
import eu.telecom_bretagne.praxis.core.execution.ActivityContext; import eu.telecom_bretagne.praxis.common.Log;
import eu.telecom_bretagne.praxis.core.workflow.Program; import eu.telecom_bretagne.praxis.core.workflow.Program;
import eu.telecom_bretagne.praxis.core.workflow.Workflow; import eu.telecom_bretagne.praxis.core.workflow.Workflow;
import eu.telecom_bretagne.praxis.server.Serveur; import eu.telecom_bretagne.praxis.server.Serveur;
...@@ -63,6 +63,21 @@ public class WorkflowPlanner ...@@ -63,6 +63,21 @@ public class WorkflowPlanner
activity.getExecutionSet().add(program); activity.getExecutionSet().add(program);
} }
// log details on the workflow being executed
try
{
StringBuffer log = new StringBuffer("Executing WF ");
log.append(workflow.id().dumpID()).append(":");
for (Program program: workflow.getPrograms())
{
log.append(" ").append(program.getDescription().id());
}
Log.log.info(log.toString());
}
catch (Throwable t)
{
Log.log.log(java.util.logging.Level.WARNING, "Unable to log info on the execution", t);
}
} }
/** /**
......
...@@ -9,6 +9,8 @@ import java.rmi.RemoteException; ...@@ -9,6 +9,8 @@ import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject; import java.rmi.server.UnicastRemoteObject;
import java.sql.Connection; import java.sql.Connection;
import java.sql.DriverManager; import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
...@@ -25,11 +27,11 @@ import eu.telecom_bretagne.praxis.common.Configuration; ...@@ -25,11 +27,11 @@ import eu.telecom_bretagne.praxis.common.Configuration;
import eu.telecom_bretagne.praxis.common.Console; import eu.telecom_bretagne.praxis.common.Console;
import eu.telecom_bretagne.praxis.common.Facade_xml; import eu.telecom_bretagne.praxis.common.Facade_xml;
import eu.telecom_bretagne.praxis.common.FileResources; import eu.telecom_bretagne.praxis.common.FileResources;
import eu.telecom_bretagne.praxis.common.FileResources.ResourceNotFoundException;
import eu.telecom_bretagne.praxis.common.InvalidXMLException; import eu.telecom_bretagne.praxis.common.InvalidXMLException;
import eu.telecom_bretagne.praxis.common.Log; import eu.telecom_bretagne.praxis.common.Log;
import eu.telecom_bretagne.praxis.common.PraxisPreferences; import eu.telecom_bretagne.praxis.common.PraxisPreferences;
import eu.telecom_bretagne.praxis.common.RemoteComponents; import eu.telecom_bretagne.praxis.common.RemoteComponents;
import eu.telecom_bretagne.praxis.common.FileResources.ResourceNotFoundException;
import eu.telecom_bretagne.praxis.common.events.RMICommunicationFacade; import eu.telecom_bretagne.praxis.common.events.RMICommunicationFacade;
import eu.telecom_bretagne.praxis.common.events.SSLSocketCommunicationFacade; import eu.telecom_bretagne.praxis.common.events.SSLSocketCommunicationFacade;
import eu.telecom_bretagne.praxis.common.events.SocketCommunicationFacade; import eu.telecom_bretagne.praxis.common.events.SocketCommunicationFacade;
...@@ -47,6 +49,13 @@ import eu.telecom_bretagne.praxis.server.execution.platform.PlatformDescription; ...@@ -47,6 +49,13 @@ import eu.telecom_bretagne.praxis.server.execution.platform.PlatformDescription;
*/ */
public class Serveur extends UnicastRemoteObject implements RemoteServerInterface, ApplicationListener public class Serveur extends UnicastRemoteObject implements RemoteServerInterface, ApplicationListener
{ {
/**
* This is used to log the number of clients connected, when a client {@link #registerClient(Client) connects} or
* {@link #removeClient(Client) disconnects}. This may be used by an external tool to monitor the number of
* connected users by examining the logs.
*/
public static final String NUMBER_OF_CLIENTS_CURRENTLY_CONNECTED = "Number of clients currently connected: ";
private static final long serialVersionUID = 6870908801969839494L; private static final long serialVersionUID = 6870908801969839494L;
/** /**
...@@ -97,6 +106,17 @@ public class Serveur extends UnicastRemoteObject implements RemoteServerInterfac ...@@ -97,6 +106,17 @@ public class Serveur extends UnicastRemoteObject implements RemoteServerInterfac
{ {
c.createStatement().execute(ResultStore.RESULT_db_schema); c.createStatement().execute(ResultStore.RESULT_db_schema);
} }
try
{
final Statement statement = c.createStatement();
statement.execute("PRAGMA synchronous = NORMAL;");
statement.execute("PRAGMA journal_mode=WAL;");
statement.close();
}
catch(SQLException e)
{
Log.log.log(Level.WARNING, "Unable to set journal_mode to WAL w/ synchronous=normal", e);
}
resultStore = new ResultStore(c); resultStore = new ResultStore(c);
} }
catch (Exception e) catch (Exception e)
...@@ -226,6 +246,7 @@ public class Serveur extends UnicastRemoteObject implements RemoteServerInterfac ...@@ -226,6 +246,7 @@ public class Serveur extends UnicastRemoteObject implements RemoteServerInterfac
Log.log.fine("Registered client: " + client.toString()); Log.log.fine("Registered client: " + client.toString());
clients.add(client); clients.add(client);
Log.log.info(NUMBER_OF_CLIENTS_CURRENTLY_CONNECTED+clients.size());
} }
/* now that the client is registered, messages can be sent safely */ /* now that the client is registered, messages can be sent safely */
clientConnected(client); clientConnected(client);
...@@ -266,7 +287,7 @@ public class Serveur extends UnicastRemoteObject implements RemoteServerInterfac ...@@ -266,7 +287,7 @@ public class Serveur extends UnicastRemoteObject implements RemoteServerInterfac
Log.log.fine("Unregistering client: null"); Log.log.fine("Unregistering client: null");
boolean ret_status = clients.remove(client); boolean ret_status = clients.remove(client);
Log.log.info("Number of clients currently connected: "+clients.size()); Log.log.info(NUMBER_OF_CLIENTS_CURRENTLY_CONNECTED+clients.size());
if (Configuration.getBoolean("server.exit_on_last_client_exit") && clients.size()==0) if (Configuration.getBoolean("server.exit_on_last_client_exit") && clients.size()==0)
{ {
for (Server platform: platforms) for (Server platform: platforms)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment