Skip to content

Commit

Permalink
[DOC] Rework JTourBus tour for "Creating a new Activity type"
Browse files Browse the repository at this point in the history
Change-Id: I5e7bc2b9b076f59bf28a74cec7c845728d3aa021
Reviewed-on: http://saros-build.imp.fu-berlin.de/gerrit/1579
Tested-by: Jenkins CI
Reviewed-by: Bernd Bieber <brotlee@zedat.fu-berlin.de>
Reviewed-by: Damla Durmaz <ddurmaz88@gmail.com>
Reviewed-by: Franz Zieris <zieris@inf.fu-berlin.de>
  • Loading branch information
fzieris committed May 28, 2014
1 parent c4fa9fe commit e73c55f
Show file tree
Hide file tree
Showing 8 changed files with 153 additions and 75 deletions.
Expand Up @@ -12,9 +12,22 @@ public abstract class AbstractActivity implements IActivity {
private final User source;

/**
* @JTourBusStop 2, Activity creation, The abstract class to extend from:
* @JTourBusStop 2, Creating a new Activity type, The abstract class to
* extend:
*
* A new activity implementation should inherit this class.
* Instead of directly implementing IActivity, any new
* activity type should extends this class.
*
* However, there is an important subtype of activities: An
* activity that refers to a resource, such as a file, should
* implement the more specialized IResourceActivity interface.
* And guess what, there is an abstract class for this
* interface, too: AbstractResourceActivity.
*
* So once you decided which of these two abstract classes to
* extend, you can provide your new class with all fields and
* methods you deem necessary, and then continue with the next
* stop.
*/

/**
Expand Down
@@ -1,19 +1,30 @@
package de.fu_berlin.inf.dpp.activities;

/**
* @JTourBusStop 6, Activity creation, Triple dispatch abstract class:
*
* Instead of having to create stubs for all IActivity implementations
* not handled you can extend this abstract class and just re-implement
* the method for your new IActivity implementation.
*/
/**
* Abstract implementation of IActivityReceiver which does nothing.
* Abstract implementation of IActivityReceiver, which does nothing.
*
* Useful, if just interested in some particular IActivity
*/
public class AbstractActivityReceiver implements IActivityReceiver {

/**
* @JTourBusStop 4, Creating a new Activity type, Triple dispatch abstract
* class:
*
* Instead of creating stubs for all receive() variants for
* IActivity types you're not even interested in, you can
* extend this abstract class and just override the one (or
* few) method(s) you actually care about.
*
* So once you added a new receive() variant to
* IActivityReceiver, make sure to add a null-implementation
* for your new activity type here, to unburden all *other*
* IActivityReceiver implementations from having to implement
* the receive() method for *your* new activity.
*/

/***/

@Override
public void receive(ChangeColorActivity changeColorActivity) {
// do nothing
Expand Down Expand Up @@ -64,6 +75,11 @@ public void receive(ProgressActivity progressActivity) {
// do nothing
}

@Override
public void receive(ShareConsoleActivity shareConsoleActivity) {
// do nothing
}

@Override
public void receive(StartFollowingActivity startFollowingActivity) {
// do nothing
Expand Down Expand Up @@ -99,8 +115,4 @@ public void receive(ViewportActivity viewportActivity) {
// do nothing
}

@Override
public void receive(ShareConsoleActivity shareConsoleActivity) {
// do nothing
}
}
Expand Up @@ -16,17 +16,6 @@
public class ChangeColorActivity extends AbstractActivity implements
ITargetedActivity {

/**
* @JTourBusStop 3, Activity creation, Creating another representation:
*
* TODO Rework this tour.
*/
/**
* @JTourBusStop 4, Activity creation, Going back to the IActivity:
*
* TODO Rework this tour.
*/

@XStreamAsAttribute
protected final User target;

Expand Down
Expand Up @@ -19,7 +19,6 @@
*/
package de.fu_berlin.inf.dpp.activities;

import de.fu_berlin.inf.dpp.activities.SPath;
import de.fu_berlin.inf.dpp.session.User;

/**
Expand All @@ -38,16 +37,19 @@
public interface IActivity {

/**
* @JTourBusStop 1, Activity creation, The activity interface:
* @JTourBusStop 1, Creating a new Activity type, The IActivity interface:
*
* This is the interface for all activity implementations,
* there is a specialization called IResourceActivity. An
* activity implementation that refers to a resource, e.g. a
* file, should implement the IResourceActivity interface.
* This tour explains what you need to consider when you want
* to create a new Activity type.
*
* The only attribute of an activity instance is the source,
* that is the user that caused the initial creation of the
* activity instance.
* IActivity is the base interface for all activity
* implementations. The only attribute of an IActivity
* instance is the source, that is the session participant who
* did "something" and therefore caused this activity in the
* first place.
*
* So create a new class in the "activities" package with the
* suffix "Activity" and continue with the next stop.
*/
/**
* @JTourBusStop 3, Some Basics:
Expand All @@ -61,6 +63,7 @@ public interface IActivity {
* Handling of Activities is done using the Inversion of
* Control pattern.
*/

/**
* Returns the user who caused this activity.
*
Expand All @@ -69,11 +72,21 @@ public interface IActivity {
public User getSource();

/**
* The activity will call the receive method of the given receiver with the
* actual type of this IActivity.
* Any implementation of IActivity needs to override this method in the
* following way:
*
* <pre>
* &#064;Override
* <b>public void</b> dispatch(IActivityReceiver receiver) {
* receiver.receive(<b>this</b>);
* }
* </pre>
*
* For instance if dispatch is called on a FolderActivity it will call
* {@link IActivityReceiver#receive(FolderActivity)}
* This way, the matching {@code receive()} method of the given
* {@code receiver} will be called, which effectively provides the
* {@code receiver} with a type-safe activity object (type-safety is also
* the reason why this method cannot be inherited from a common super class,
* although the characters of the method body are always the same).
*/
public void dispatch(IActivityReceiver receiver);

Expand Down
@@ -1,58 +1,63 @@
package de.fu_berlin.inf.dpp.activities;

/**
* @JTourBusStop 5, Activity creation, Triple dispatch interface:
*
* The below interface is used for the triple dispatch of the
* activity providers. For a new IActivity implementation you will
* need to add a new receive overload to the interface and fix the
* compilation in the AbstractActivityReceiver class.
*/
/**
* A Receiver is an interface for handling multiple dispatch in Java.
*
* In our case we want to call one of the specialized receive methods in
* IActivityReceiver for a given IActivity.
*
* For instance, if an IActivity is a TextSelectionActivity, we want the method
* This interface is for implementing multiple dispatch in Java (see <a
* href="http://en.wikipedia.org/wiki/Multiple_dispatch">Wikipedia entry</a>).
* <p>
* In our case we want to call one of the specialized {@code receive()} methods
* in {@link IActivityReceiver} for a given {@link IActivity}. For instance, if
* an activity is a {@link TextSelectionActivity}, we want the method
* {@link #receive(TextSelectionActivity)} to be called.
*
*/
public interface IActivityReceiver {
/**
* @JTourBusStop 3, Creating a new Activity type, Triple dispatch interface:
*
* This interface provides a receive() method for each
* activity implementation. This is part of the so-called
* "triple dispatch" (see "Activity sending" tour). For now,
* it is enough to know that you need to declare a new
* receive() method with your new Activity type in this
* interface (follow the alphabet, please), and then go to the
* next stop in this tour.
*/

/***/

void receive(ViewportActivity viewportActivity);

void receive(TextSelectionActivity textSelectionActivity);
void receive(ChangeColorActivity changeColorActivity);

void receive(TextEditActivity textEditActivity);
void receive(ChecksumActivity checksumActivity);

void receive(PermissionActivity permissionActivity);
void receive(ChecksumErrorActivity checksumErrorActivity);

void receive(FolderActivity folderActivity);
void receive(EditorActivity editorActivity);

void receive(FileActivity fileActivity);

void receive(EditorActivity editorActivity);
void receive(FolderActivity folderActivity);

void receive(JupiterActivity jupiterActivity);

void receive(StopActivity stopActivity);

void receive(ChecksumActivity checksumActivity);
void receive(NOPActivity nopActivity);

void receive(ChecksumErrorActivity checksumErrorActivity);
void receive(PermissionActivity permissionActivity);

void receive(ProgressActivity progressActivity);

void receive(VCSActivity activity);

void receive(ChangeColorActivity changeColorActivity);
void receive(ShareConsoleActivity shareConsoleActivity);

void receive(StartFollowingActivity startFollowingActivity);

void receive(StopActivity stopActivity);

void receive(StopFollowingActivity stopFollowingActivity);

void receive(NOPActivity nopActivity);
void receive(TextEditActivity textEditActivity);

void receive(TextSelectionActivity textSelectionActivity);

void receive(ViewportActivity viewportActivity);

void receive(VCSActivity activity);

void receive(ShareConsoleActivity shareConsoleActivity);
}
@@ -1,6 +1,5 @@
package de.fu_berlin.inf.dpp.activities;


/**
* An interface for Activities that are resource-related (e.g. FileActivity)
*/
Expand Down
Expand Up @@ -118,13 +118,25 @@ public int getSequenceNumber() {
}

/**
* @JTourBusStop 7, Activity creation, {@link IActivity} registration:
* @JTourBusStop 5, Creating a new Activity type, XStream registration:
*
* All {@link IActivity} implementations should be registered
* with the XStream extensions provider, otherwise annotations
* like XStreamAlias will not be honored.
* We use the XStream library to convert handy Java objects to
* easy-to-send XML string and vice versa. To beautify this
* XML output, we make use of annotations (such as
* XStreamAlias or XStreamAsAttribute).
*
* To make a long story short: Just add your new activity type
* to the list below, so any annotations you might want to use
* will be honored. And again, please remember the alphabet.
*
* Since you now know about XStream annotations, you might
* want to go back to your new class and add some of these?
* You can take a look at other activity classes for
* inspiration.
*/

/***/

public static class Provider extends
SarosSessionPacketExtension.Provider<ActivitiesExtension> {
private Provider() {
Expand Down
Expand Up @@ -55,13 +55,13 @@

import de.fu_berlin.inf.dpp.activities.AbstractActivityReceiver;
import de.fu_berlin.inf.dpp.activities.EditorActivity;
import de.fu_berlin.inf.dpp.activities.EditorActivity.Type;
import de.fu_berlin.inf.dpp.activities.IActivity;
import de.fu_berlin.inf.dpp.activities.IActivityReceiver;
import de.fu_berlin.inf.dpp.activities.SPath;
import de.fu_berlin.inf.dpp.activities.TextEditActivity;
import de.fu_berlin.inf.dpp.activities.TextSelectionActivity;
import de.fu_berlin.inf.dpp.activities.ViewportActivity;
import de.fu_berlin.inf.dpp.activities.EditorActivity.Type;
import de.fu_berlin.inf.dpp.annotations.Component;
import de.fu_berlin.inf.dpp.editor.RemoteEditorManager.RemoteEditor;
import de.fu_berlin.inf.dpp.editor.RemoteEditorManager.RemoteEditorState;
Expand Down Expand Up @@ -192,6 +192,23 @@ public void propertyChange(final PropertyChangeEvent event) {
}
};

/**
* @JTourBusStop 7, Creating a new Activity type, Waiting for incoming
* activities:
*
* All you have to do on the receiver's side, is to create a
* new AbstractActivityReceiver (or amend an existing one),
* provide it with an receive() method of your newly created
* flavor, and react on the incoming activity.
*
* In case you had to create a new AbstractActivityReceiver
* (because there was no existing one), you'll need to extend
* AbstractActivityProvider, so you can override the exec()
* method and call "activity.dispatch(yourActivityReceiver)".
*/

/***/

private IActivityReceiver activityReceiver = new AbstractActivityReceiver() {
@Override
public void receive(EditorActivity editorActivity) {
Expand Down Expand Up @@ -574,6 +591,24 @@ public void generateEditorActivated(@Nullable SPath path) {
editorListenerDispatch.activeEditorChanged(sarosSession.getLocalUser(),
path);

/**
* @JTourBusStop 6, Creating a new Activity type, Create activity
* instances of your new type:
*
* Now you are prepared to make use of your new activity
* type: Find a place in the business logic where to react
* on the events you want to send as an Activity to the
* other session participants. However, it is not unusual
* to create that piece of business logic anew.
*
* Anyway, once you found a place where to wait for
* certain things to happen, you can create new activity
* instances of your type there and hand them over to
* fireActivity() -- assuming your business logic class
* extends AbstractActivityProvider, of course. That's all
* for the sender's side.
*/

fireActivity(new EditorActivity(sarosSession.getLocalUser(),
Type.ACTIVATED, path));

Expand Down

0 comments on commit e73c55f

Please sign in to comment.