This is an automated email from the ASF dual-hosted git repository. juanpablo pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/jspwiki.git
commit a0505787b880145c6bfe90fd51368d00273328ba Author: juanpablo <[email protected]> AuthorDate: Tue May 5 21:06:26 2020 +0200 JSPWIKI-304: un/serialize Workflows + Decision Queue from/to disk --- .../wiki/workflow/DefaultWorkflowManager.java | 75 ++++++++++++++++++++-- .../apache/wiki/workflow/WorkflowManagerTest.java | 34 +++++++++- 2 files changed, 102 insertions(+), 7 deletions(-) diff --git a/jspwiki-main/src/main/java/org/apache/wiki/workflow/DefaultWorkflowManager.java b/jspwiki-main/src/main/java/org/apache/wiki/workflow/DefaultWorkflowManager.java index fb6e648..b1444f4 100644 --- a/jspwiki-main/src/main/java/org/apache/wiki/workflow/DefaultWorkflowManager.java +++ b/jspwiki-main/src/main/java/org/apache/wiki/workflow/DefaultWorkflowManager.java @@ -18,6 +18,7 @@ */ package org.apache.wiki.workflow; +import org.apache.commons.lang3.time.StopWatch; import org.apache.log4j.Logger; import org.apache.wiki.api.core.Engine; import org.apache.wiki.api.core.Session; @@ -28,6 +29,14 @@ import org.apache.wiki.event.WikiEvent; import org.apache.wiki.event.WikiEventEmitter; import org.apache.wiki.event.WorkflowEvent; +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; import java.security.Principal; import java.util.ArrayList; import java.util.List; @@ -48,11 +57,15 @@ import java.util.concurrent.CopyOnWriteArrayList; public class DefaultWorkflowManager implements WorkflowManager { private static final Logger LOG = Logger.getLogger( DefaultWorkflowManager.class ); + static final String SERIALIZATION_FILE = "wkflmgr.ser"; - private final DecisionQueue m_queue = new DecisionQueue(); - private final Set< Workflow > m_workflows; - private final Map< String, Principal > m_approvers; - private final List< Workflow > m_completed; + /** We use this also a generic serialization id */ + private static final long serialVersionUID = 6L; + + DecisionQueue m_queue = new DecisionQueue(); + Set< Workflow > m_workflows; + final Map< String, Principal > m_approvers; + List< Workflow > m_completed; private Engine m_engine = null; /** @@ -111,6 +124,59 @@ public class DefaultWorkflowManager implements WorkflowManager { } } } + + unserializeFromDisk( new File( m_engine.getWorkDir(), SERIALIZATION_FILE ) ); + } + + /** + * Reads the serialized data from the disk back to memory. + * + * @return the date when the data was last written on disk or {@code 0} if there has been problems reading from disk. + */ + @SuppressWarnings( "unchecked" ) + synchronized long unserializeFromDisk( final File f ) { + long saved = 0L; + final StopWatch sw = new StopWatch(); + sw.start(); + try( final ObjectInputStream in = new ObjectInputStream( new BufferedInputStream( new FileInputStream( f ) ) ) ) { + final long ver = in.readLong(); + if( ver != serialVersionUID ) { + LOG.warn( "File format has changed; Unable to recover workflows and decision queue from disk." ); + } else { + saved = in.readLong(); + m_workflows = ( Set< Workflow > )in.readObject(); + m_queue = ( DecisionQueue )in.readObject(); + m_completed = ( List< Workflow > )in.readObject(); + LOG.debug( "Read serialized data successfully in " + sw ); + } + } catch( final IOException | ClassNotFoundException e ) { + LOG.error( "unable to recover from disk workflows and decision queue: " + e.getMessage(), e ); + } + sw.stop(); + + return saved; + } + + /** + * Serializes workflows and decisionqueue to disk. The format is private, don't touch it. + */ + synchronized void serializeToDisk( final File f ) { + try( final ObjectOutputStream out = new ObjectOutputStream( new BufferedOutputStream( new FileOutputStream( f ) ) ) ) { + final StopWatch sw = new StopWatch(); + sw.start(); + + out.writeLong( serialVersionUID ); + out.writeLong( System.currentTimeMillis() ); // Timestamp + out.writeObject( m_workflows ); + out.writeObject( m_queue ); + out.writeObject( m_completed ); + + sw.stop(); + + LOG.debug( "serialization done - took " + sw ); + } catch( final IOException ioe ) { + LOG.error( "Unable to serialize!", ioe ); + } } /** @@ -220,6 +286,7 @@ public class DefaultWorkflowManager implements WorkflowManager { default: break; } } + serializeToDisk( new File( m_engine.getWorkDir(), SERIALIZATION_FILE ) ); } } diff --git a/jspwiki-main/src/test/java/org/apache/wiki/workflow/WorkflowManagerTest.java b/jspwiki-main/src/test/java/org/apache/wiki/workflow/WorkflowManagerTest.java index 5ec58c7..6735029 100644 --- a/jspwiki-main/src/test/java/org/apache/wiki/workflow/WorkflowManagerTest.java +++ b/jspwiki-main/src/test/java/org/apache/wiki/workflow/WorkflowManagerTest.java @@ -23,20 +23,22 @@ import org.apache.wiki.WikiEngine; import org.apache.wiki.api.exceptions.WikiException; import org.apache.wiki.auth.GroupPrincipal; import org.apache.wiki.auth.WikiPrincipal; +import org.apache.wiki.event.WorkflowEvent; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import java.io.File; + public class WorkflowManagerTest { - protected Workflow w; - protected WorkflowManager wm; protected WikiEngine m_engine = TestEngine.build(); + protected WorkflowManager wm = m_engine.getManager( WorkflowManager.class ); + protected Workflow w; @BeforeEach public void setUp() throws Exception { - wm = m_engine.getManager( WorkflowManager.class ); // Create a workflow with 3 steps, with a Decision in the middle w = new Workflow( "workflow.key", new WikiPrincipal( "Owner1" ) ); final Step startTask = new TaskTest.NormalTask( w ); @@ -97,4 +99,30 @@ public class WorkflowManagerTest { Assertions.assertThrows( WikiException.class, () -> wm.getApprover( "workflow.saveWikiPage" ) ); } + @Test + public void testSerializeUnserialize() throws WikiException { + final DefaultWorkflowManager dwm = new DefaultWorkflowManager(); + dwm.initialize( m_engine, TestEngine.getTestProperties() ); + + dwm.unserializeFromDisk( new File( "./target/test-classes", DefaultWorkflowManager.SERIALIZATION_FILE ) ); + Assertions.assertEquals( 1, dwm.m_workflows.size() ); + Assertions.assertEquals( 1, dwm.m_queue.decisions().length ); + Assertions.assertEquals( 0, dwm.m_completed.size() ); + + final Workflow workflow = dwm.m_workflows.iterator().next(); + final Decision d = ( Decision )workflow.getCurrentStep(); + d.decide( Outcome.DECISION_APPROVE ); + dwm.actionPerformed( new WorkflowEvent( workflow, WorkflowEvent.COMPLETED ) ); + dwm.actionPerformed( new WorkflowEvent( d, WorkflowEvent.DQ_REMOVAL ) ); + Assertions.assertEquals( 0, dwm.getWorkflows().size() ); + Assertions.assertEquals( 0, dwm.m_queue.decisions().length ); + Assertions.assertEquals( 1, dwm.getCompletedWorkflows().size() ); + dwm.serializeToDisk( new File( "./target/test-classes", DefaultWorkflowManager.SERIALIZATION_FILE ) ); + + dwm.unserializeFromDisk( new File( "./target/test-classes", DefaultWorkflowManager.SERIALIZATION_FILE ) ); + Assertions.assertEquals( 0, dwm.m_workflows.size() ); + Assertions.assertEquals( 0, dwm.m_queue.decisions().length ); + Assertions.assertEquals( 1, dwm.m_completed.size() ); + } + }
