Merge remote-tracking branch 'origin/master' into bug_1961

This commit is contained in:
fmittag committed 2014-05-22 09:52:28 +02:00
commit 050f410f33
44 files changed
+5528 -4349

No files matched your search

@@ -0,0 +1,104 @@
# Working with GWT UI Binder
[[_TOC_]]
## Introduction
The GWT UI Binder technology uses HTML-like documents to describe the structure and static content of the UI. See the tutorial here: http://www.gwtproject.org/doc/latest/DevGuideUiBinder.html. With UI Binder, widgets are developed by declaring the widget's static HTML content in a `.ui.xml` file that more or less looks like HTML. By naming convention, a corresponding class related to the `.ui.xml` file. The class can bind its fields to elements in the `.ui.xml` file using Java annotations such as `@UiField`, matching with `ui:field` attributes in the `.ui.xml` file.
In the `.ui.xml` flie, elements can be used to represent widgets. In particular, all predefined GWT widgets are usually mapped to the `g:` namespace and can be used as in `<g:FlowPanel ui:field="someFieldName" />`. This will bind an instance of type `FlowPanel` to the widget class's field named `someFieldName` which has to be of type `com.google.gwt.user.client.ui.FlowPanel`.
## The Design and Development Process with Ubilabs
We chose to work with a partner for UI Design: Ubilabs in Hamburg, Germany. They use a framework called Jade for their Web development. Their git repository lives at `https://<yourgithubuser>@github.com/ubilabs/sap-sailing-analytics.git`. The output of the Ubilabs build process is a bunch of HTML files (matching the "partials" component structure used in Jade) and the corresponding CSS files. Ubilabs plans to commit this generated output regularly to their git repository so we can understand the changes that we need to map into our environment.
As Ubilabs does not use GWT, we need to map their build outputs to our GWT architecture. GWT UI Binder fits with this goal. For each Jade partial we develop a corresponding UI Binder widget. The CSS file generated by Ubilabs can be used as is for the UI Binder widget. The HTML file needs to be renamed to `.ui.xml` and its contents need to be adjusted to the UI Binder styles and elements.
We asked Ubilabs whether they can instrument their Jade-based generator in such a way that for each Jade Partial `X` that will become a GWT UI Binder Widget we will automatically receive an `XResources.java` file that contains the CssResource interface declaration for the widget's CSS with a @Source annotation and a `css()` method declaration and an INSTANCE field declaration as more or less static content which fits in with how GWT ideally handles CSS resources. In the generated `X.ui.xml` file we then expect the CSS resource to be declared as in
<pre>
&lt;ui:with field="local_res" type="com.sap.sailing.gwt.home...XResources" /&gt;
</pre>
which can be used as in
<pre>
&lt;header class="{local_res.css.siteheader}"&gt;
</pre>
Ubilabs will deliver two versions of the site: one for use on desktops and tablets, and another for smartphone use. We need to map both versions to GWT UI Binder and decide based on the user agent and viewport size which one to choose.
## Internationalization (i18n)
We're not yet decided on how to implement i18n. GWT offers two mechanisms that seem viable, see http://www.gwtproject.org/doc/latest/DevGuideUiBinderI18n.html. We could use GWT's feature that generates the property files. The upside would be that in case of many static texts we would get a quick start. The generation of UUIDs from the original static texts additionally ensures that changes cause compile errors and hence notify us that re-translation is required, which is good.
The downside is that redundancies across different entry points are not handled well and that the property files may not fit in well with programmatic use of those message strings.
Alternatively, it is possible to declare the string messages class and the method name that shall be used for the string message property. This way, we could consolidate all texts into a single message properties file which may help avoid redundancies.
We'll probably try out both approaches in a small scale and then decide.
## GWT Code Splitting
For the various areas of the site we don't always want to have to load a new page. Instead, we'd like to use the "places" pattern with a local history management. This goes together well with GWT Code Splitting (see http://www.gwtproject.org/doc/latest/DevGuideCodeSplitting.html#patterns) which allows an application to load its parts when they are needed. This speeds up the initial loading process and keeps bandwidth consumption low. We use the Gin framework to support us in this.
## About GWTP and the Gin Framework and how We Use Them
GWPT (https://github.com/ArcBees/GWTP and https://github.com/arcbees/gwtp/wiki) is a model-view-presenter framework that uses Gin (GWT INjection, see https://code.google.com/p/google-gin/ and the tutorial at https://code.google.com/p/google-gin/wiki/GinTutorial) as an underlying framework for GWT-based dependency injection.
### Our Bundle and Package Structure
The key bundle to the GWT UI Binder-based site is `com.sap.sailing.gwt.home`. The key module is described in `src/main/resources` under `com/sap/sailing/gwt/home/Home.gwt.xml`. It contains configuration entries of the form
<pre>
&lt;set-configuration-property name="gin.ginjector.module.desktop"
value="com.sap.sailing.gwt.home.client.gin.DesktopModule" /&gt;
&lt;set-configuration-property name="gin.ginjector.module.mobile"
value="com.sap.sailing.gwt.home.client.gin.MobileModule" /&gt;
&lt;set-configuration-property name="gin.ginjector.module.tablet"
value="com.sap.sailing.gwt.home.client.gin.TabletModule" /&gt;
</pre>
which instructs Gin to activate the modules mentioned for the respective type of user agent (desktop, mobile, tablet). Each such module implementation is trivial, as seen in the desktop module:
<pre>
public class DesktopModule extends AbstractPresenterModule {
@Override
protected void configure() {
install(new ApplicationDesktopModule());
}
}
</pre>
As the example shows, the presenter module redirects by installing another module, showing that modules can be nested. A nested module, such as `ApplicationDesktopModule` then binds views to presenters, as in
<pre>
bindPresenter(StartPagePresenter.class, StartPagePresenter.MyView.class, StartPageView.class,
StartPagePresenter.MyProxy.class);
</pre>
Note the use of `MyProxy`. This is where GWT's code splitting capability comes into play.
### Basic Use of GWTP
The basic principles of GWTP suggest the use of a presenter, a view and a proxy that act together as a component. The view implements an interface declared in the presenter, usually called `MyView` which among other things may allow the presenter to update data to the view.
The view class finally binds the GWTP component to the UI Binder logic. It extends some `Widget` subclass and implements the view interface declared by the presenter. The view class also defines a `UiBinder` subinterface which is used in a call to `GWT.create(...)`. Here is an example:
<pre>
public class EventsPageView extends Composite implements EventsPagePresenter.MyView {
private static EventsPageViewUiBinder uiBinder = GWT.create(EventsPageViewUiBinder.class);
interface EventsPageViewUiBinder extends UiBinder<Widget, EventsPageView> {
}
...
@Inject
public EventsPageView(PlaceManager placeManager) {
super();
... // initialize component and fields
initWidget(uiBinder.createAndBindUi(this));
... // do more stuff after widget has been bound to .ui.xml
}
</pre>
+1
View File
@@ -24,6 +24,7 @@ Like businesses, sailors need the latest information to make strategic decisions
* [[Server Replication|wiki/server-replication]]
* [[Configure Races on Server|wiki/typical-development-scenarios]]
* [[Development Environment|wiki/development-environment]]
* [[Working with GWT UI Binder/wiki/gwt-ui-binder]]
* [[Working with GWT Locally|wiki/local-gwt]]
* [[Mobile Development|wiki/mobile-development]]
* Information for Webdesigners
@@ -49,18 +49,26 @@ public class LeaderboardDTO extends AbstractLeaderboardDTO implements Serializab
*/
private boolean higherScoresIsBetter;
/**
* Tells whether this leaderboard has data about overall details. This corresponds with the <code>addOverallDetails</code> request parameter
* for a leaderboard DTO computation.
*/
private boolean hasOverallDetails;
LeaderboardDTO() {} // for serialization
/**
* @param uuidGenerator used to provide the {@link #id ID} for this object (see also {@link #getId()}) and for any clones produced
* from it by the {@link #clone()} operation.
* @param hasOverallDetails TODO
*/
public LeaderboardDTO(Date timePointOfLastCorrectionsValidity, String comment, boolean higherScoreIsBetter, UUIDGenerator uuidGenerator) {
public LeaderboardDTO(Date timePointOfLastCorrectionsValidity, String comment, boolean higherScoreIsBetter, UUIDGenerator uuidGenerator, boolean hasOverallDetails) {
initCollections();
id = uuidGenerator.generateRandomUUID();
this.timePointOfLastCorrectionsValidity = timePointOfLastCorrectionsValidity;
this.comment = comment;
this.higherScoresIsBetter = higherScoreIsBetter;
this.hasOverallDetails = hasOverallDetails;
}
private void initCollections() {
@@ -165,4 +173,12 @@ public class LeaderboardDTO extends AbstractLeaderboardDTO implements Serializab
public void setComment(String comment) {
this.comment = comment;
}
/**
* Tells whether this leaderboard has data about overall details. This corresponds with the <code>addOverallDetails</code> request parameter
* for a leaderboard DTO computation.
*/
public boolean hasOverallDetails() {
return hasOverallDetails;
}
}
@@ -2,7 +2,9 @@ package com.sap.sailing.domain.common.dto;
import java.io.Serializable;
public class NamedDTO implements Serializable {
import com.sap.sailing.domain.common.Named;
public class NamedDTO implements Named, Serializable {
private static final long serialVersionUID = -6620231501480297257L;
private String name;
@@ -16,13 +16,20 @@ public class TimeRangeImpl extends Pair<TimePoint, TimePoint> implements TimeRan
public TimeRangeImpl(TimePoint from, TimePoint to) {
super(from == null ? BeginningOfTime : from, to == null ? EndOfTime : to);
if (from().after(to())) throw new IllegalArgumentException(String.format("from (%s) must lie before to (%s) in a TimeRange", from(), to()));
if (from().after(to())) {
throw new IllegalArgumentException("from " + from() + " must lie before to " + to() + " in a TimeRange");
}
}
@Override
public int compareTo(TimeRange other) {
if (other.from().equals(from()) && other.to().equals(to())) return 0;
return startsBefore(other) ? -1 : 1;
final int result;
if (other.from().equals(from()) && other.to().equals(to())) {
result = 0;
} else {
result = startsBefore(other) ? -1 : 1;
}
return result;
}
@Override
@@ -80,10 +87,15 @@ public class TimeRangeImpl extends Pair<TimePoint, TimePoint> implements TimeRan
@Override
public TimeRange union(TimeRange other) {
if (! intersects(other)) return null;
TimePoint newFrom = startsBefore(other) ? from() : other.from();
TimePoint newTo = endsAfter(other) ? to() : other.to();
return new TimeRangeImpl(newFrom, newTo);
final TimeRange result;
if (!intersects(other)) {
result = null;
} else {
TimePoint newFrom = startsBefore(other) ? from() : other.from();
TimePoint newTo = endsAfter(other) ? to() : other.to();
result = new TimeRangeImpl(newFrom, newTo);
}
return result;
}
@Override
@@ -98,20 +110,30 @@ public class TimeRangeImpl extends Pair<TimePoint, TimePoint> implements TimeRan
@Override
public TimeRange intersection(TimeRange other) {
if (! intersects(other)) return null;
TimePoint newFrom = startsBefore(other) ? other.from() : from();
TimePoint newTo = endsAfter(other) ? other.to() : to();
return new TimeRangeImpl(newFrom, newTo);
final TimeRange result;
if (!intersects(other)) {
result = null;
} else {
TimePoint newFrom = startsBefore(other) ? other.from() : from();
TimePoint newTo = endsAfter(other) ? other.to() : to();
result = new TimeRangeImpl(newFrom, newTo);
}
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj instanceof TimeRange) {
TimeRange other = (TimeRange) obj;
return from().equals(other.from()) && to().equals(other.to());
final boolean result;
if (this == obj) {
result = true;
} else {
if (obj instanceof TimeRange) {
TimeRange other = (TimeRange) obj;
return from().equals(other.from()) && to().equals(other.to());
}
result = super.equals(obj);
}
return super.equals(obj);
return result;
}
@Override
@@ -9,6 +9,8 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import com.sap.sailing.domain.common.Named;
public class Util {
/**
* Adds all elements from <code>what</code> to <code>addTo</code> and returns <code>addTo</code> for chained use.
@@ -26,7 +28,7 @@ public class Util {
}
}
public static <T> T[] toArray(Iterable<T> what, T[] arr) {
public static <T> T[] toArray(Iterable<? extends T> what, T[] arr) {
List<T> list = new ArrayList<T>();
addAll(what, list);
return list.toArray(arr);
@@ -290,4 +292,30 @@ public class Util {
}
map.get(key).add(value);
}
public static String join(String separator, Iterable<? extends Named> nameds) {
return join(separator, toArray(nameds, new Named[size(nameds)]));
}
public static String join(String separator, Named... nameds) {
String[] strings = new String[nameds.length];
for (int i=0; i<nameds.length; i++) {
strings[i] = nameds[i].getName();
}
return join(separator, strings);
}
public static String join(String separator, String...strings) {
StringBuilder result = new StringBuilder();
boolean first = true;
for (String string : strings) {
if (first) {
first = false;
} else {
result.append(separator);
}
result.append(string);
}
return result.toString();
}
}
@@ -11,6 +11,7 @@ import com.sap.sailing.domain.base.Event;
import com.sap.sailing.domain.base.RaceDefinition;
import com.sap.sailing.domain.base.Regatta;
import com.sap.sailing.domain.base.RegattaRegistry;
import com.sap.sailing.domain.base.RemoteSailingServerReference;
import com.sap.sailing.domain.base.configuration.DeviceConfiguration;
import com.sap.sailing.domain.base.configuration.DeviceConfigurationMatcher;
import com.sap.sailing.domain.common.RaceIdentifier;
@@ -85,6 +86,8 @@ public interface DomainObjectFactory {
Iterable<Event> loadAllEvents();
Iterable<RemoteSailingServerReference> loadAllRemoteSailingServerReferences();
Regatta loadRegatta(String name, TrackedRegattaRegistry trackedRegattaRegistry);
Iterable<Regatta> loadAllRegattas(TrackedRegattaRegistry trackedRegattaRegistry);
@@ -1,114 +1,123 @@
package com.sap.sailing.domain.persistence;
import java.io.Serializable;
import com.mongodb.DB;
import com.mongodb.DBObject;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.Event;
import com.sap.sailing.domain.base.RaceDefinition;
import com.sap.sailing.domain.base.Regatta;
import com.sap.sailing.domain.base.Series;
import com.sap.sailing.domain.base.configuration.DeviceConfiguration;
import com.sap.sailing.domain.base.configuration.DeviceConfigurationMatcher;
import com.sap.sailing.domain.common.WindSource;
import com.sap.sailing.domain.leaderboard.Leaderboard;
import com.sap.sailing.domain.leaderboard.LeaderboardGroup;
import com.sap.sailing.domain.racelog.RaceLogIdentifier;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.domain.tracking.TrackedRegatta;
/**
* Offers methods to construct {@link DBObject MongoDB objects} from domain objects.
*
* @author Axel Uhl (d043530)
*
*/
public interface MongoObjectFactory {
/**
* Registers for changes of the wind coming from <code>windSource</code> on the <code>trackedRace</code>. Each
* update received will be appended to the MongoDB and can later be retrieved. The key used to identify the race is
* the {@link RaceDefinition#getName() race name} and the {@link Regatta#getName() regatta name}.
*/
void addWindTrackDumper(TrackedRegatta trackedRegatta, TrackedRace trackedRace, WindSource windSource);
/**
* Stores the configuration data of <code>leaderboard</code> in the Mongo DB associated with this
* factory.
*/
void storeLeaderboard(Leaderboard leaderboard);
/**
* Removes the leaderboard named <code>name</code> from the database.
*/
void removeLeaderboard(String leaderboardName);
void renameLeaderboard(String oldName, String newName);
/**
* Stores the group, if it doesn't exist or updates it. Leaderboards in the group, which aren't stored in the
* database, will be stored. If the leaderboard group has an {@link LeaderboardGroup#getOverallLeaderboard() overall
* leaderboard}, it will be stored / updated as well.
*/
void storeLeaderboardGroup(LeaderboardGroup leaderboardGroup);
/**
* Removes the group with the name <code>groupName</code> from the database.
*/
void removeLeaderboardGroup(String groupName);
/**
* Renames the group with the name <code>oldName</code>.
*/
void renameLeaderboardGroup(String oldName, String newName);
/**
* Stores the event with its name, venue and the venue's course areas. The regattas obtained by
* {@link Event#getRegattas()} are <em>not</em> stored by this call. They need to be stored separately
* by calls to {@link #storeRegatta} where a reference to their owning event is stored.
*/
void storeEvent(Event event);
/**
* Renames the event with the name <code>oldName</code>.
*/
void renameEvent(Serializable id, String newName);
/**
* Removes the event named <code>eventName</code> from the database.
*/
void removeEvent(Serializable id);
/**
* Stores the regatta together with its name, {@link Series} definitions and an optional link to the
* {@link Event} to which the regatta belongs.
* @param oldSeriesNameNewName
*/
void storeRegatta(Regatta regatta);
void removeRegatta(Regatta regatta);
void storeRegattaForRaceID(String id, Regatta regatta);
void removeRegattaForRaceID(String raceIDAsString, Regatta regatta);
/**
* Stores a competitor, including the team and boat. This should not be done for competitors for which
* the master data is supplied by other systems, such as TracTrac, but rather for smartphone tracking,
* where this data is otherwise not recoverable.
* @param competitor the competitor to store/update in the database
*/
void storeCompetitor(Competitor competitor);
void removeAllCompetitors();
void removeCompetitor(Competitor competitor);
DB getDatabase();
void storeDeviceConfiguration(DeviceConfigurationMatcher matcher, DeviceConfiguration configuration);
void removeDeviceConfiguration(DeviceConfigurationMatcher matcher);
void removeRaceLog(RaceLogIdentifier identifier);
}
package com.sap.sailing.domain.persistence;
import java.io.Serializable;
import com.mongodb.DB;
import com.mongodb.DBObject;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.Event;
import com.sap.sailing.domain.base.RaceDefinition;
import com.sap.sailing.domain.base.Regatta;
import com.sap.sailing.domain.base.RemoteSailingServerReference;
import com.sap.sailing.domain.base.Series;
import com.sap.sailing.domain.base.configuration.DeviceConfiguration;
import com.sap.sailing.domain.base.configuration.DeviceConfigurationMatcher;
import com.sap.sailing.domain.common.WindSource;
import com.sap.sailing.domain.leaderboard.Leaderboard;
import com.sap.sailing.domain.leaderboard.LeaderboardGroup;
import com.sap.sailing.domain.racelog.RaceLogIdentifier;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.domain.tracking.TrackedRegatta;
/**
* Offers methods to construct {@link DBObject MongoDB objects} from domain objects.
*
* @author Axel Uhl (d043530)
*
*/
public interface MongoObjectFactory {
/**
* Registers for changes of the wind coming from <code>windSource</code> on the <code>trackedRace</code>. Each
* update received will be appended to the MongoDB and can later be retrieved. The key used to identify the race is
* the {@link RaceDefinition#getName() race name} and the {@link Regatta#getName() regatta name}.
*/
void addWindTrackDumper(TrackedRegatta trackedRegatta, TrackedRace trackedRace, WindSource windSource);
/**
* Stores the configuration data of <code>leaderboard</code> in the Mongo DB associated with this
* factory.
*/
void storeLeaderboard(Leaderboard leaderboard);
/**
* Removes the leaderboard named <code>name</code> from the database.
*/
void removeLeaderboard(String leaderboardName);
void renameLeaderboard(String oldName, String newName);
/**
* Stores the group, if it doesn't exist or updates it. Leaderboards in the group, which aren't stored in the
* database, will be stored. If the leaderboard group has an {@link LeaderboardGroup#getOverallLeaderboard() overall
* leaderboard}, it will be stored / updated as well.
*/
void storeLeaderboardGroup(LeaderboardGroup leaderboardGroup);
/**
* Removes the group with the name <code>groupName</code> from the database.
*/
void removeLeaderboardGroup(String groupName);
/**
* Renames the group with the name <code>oldName</code>.
*/
void renameLeaderboardGroup(String oldName, String newName);
/**
* Stores the event with its name, venue and the venue's course areas. The regattas obtained by
* {@link Event#getRegattas()} are <em>not</em> stored by this call. They need to be stored separately
* by calls to {@link #storeRegatta} where a reference to their owning event is stored.
*/
void storeEvent(Event event);
/**
* Renames the event with the name <code>oldName</code>.
*/
void renameEvent(Serializable id, String newName);
/**
* Removes the event named <code>eventName</code> from the database.
*/
void removeEvent(Serializable id);
/**
* Stores a registered sailing server
* @param serves the servers to store
*/
void storeSailingServer(RemoteSailingServerReference server);
void removeSailingServer(String name);
/**
* Stores the regatta together with its name, {@link Series} definitions and an optional link to the
* {@link Event} to which the regatta belongs.
* @param oldSeriesNameNewName
*/
void storeRegatta(Regatta regatta);
void removeRegatta(Regatta regatta);
void storeRegattaForRaceID(String id, Regatta regatta);
void removeRegattaForRaceID(String raceIDAsString, Regatta regatta);
/**
* Stores a competitor, including the team and boat. This should not be done for competitors for which
* the master data is supplied by other systems, such as TracTrac, but rather for smartphone tracking,
* where this data is otherwise not recoverable.
* @param competitor the competitor to store/update in the database
*/
void storeCompetitor(Competitor competitor);
void removeAllCompetitors();
void removeCompetitor(Competitor competitor);
DB getDatabase();
void storeDeviceConfiguration(DeviceConfigurationMatcher matcher, DeviceConfiguration configuration);
void removeDeviceConfiguration(DeviceConfigurationMatcher matcher);
void removeRaceLog(RaceLogIdentifier identifier);
}
@@ -30,6 +30,11 @@ public enum CollectionNames {
*/
EVENTS,
/**
* Stores the registered sailing servers.
*/
SAILING_SERVERS,
/**
* Stores regatta definitions including their series layout and fleets and race columns. Regattas can reference
* the event from the {@link #EVENTS} collection to which they belong.
@@ -39,7 +39,7 @@ public enum FieldNames {
VENUE,
VENUE_NAME,
COURSE_AREAS,
COURSE_AREA_NAME,
COURSE_AREA_ID,
@@ -84,7 +84,10 @@ public enum FieldNames {
/** @deprecated Replaced by CONTROLPOINTWITHTWOMARKS_NAME, still used to migrate old races*/ @Deprecated GATE_NAME,
/** @deprecated Replaced by CONTROLPOINTWITHTWOMARKS_LEFT, still used to migrate old races*/ @Deprecated GATE_LEFT,
/** @deprecated Replaced by CONTROLPOINTWITHTWOMARKS_RIGHT, still used to migrate old races*/ @Deprecated GATE_RIGHT,
// sailing server
SERVER_NAME,
SERVER_URL,
// configurations
CONFIGURATION_MATCHER, CONFIGURATION_CONFIG, CONFIGURATION_MATCHER_ID, CONFIGURATION_MATCHER_TYPE, CONFIGURATION_MATCHER_CLIENTS,
@@ -47,7 +47,7 @@ public class RaceLogImpl extends TrackImpl<RaceLogEvent> implements RaceLog {
private static final long serialVersionUID = -176745401321893502L;
private static final String DefaultLockName = RaceLogImpl.class.getName() + ".lock";
private final static Logger logger = Logger.getLogger(RaceLogImpl.class.getName());
private final Set<Serializable> revokedEventIds = new HashSet<Serializable>();
private Set<Serializable> revokedEventIds = new HashSet<Serializable>();
/**
* Clients can use the {@link #add(RaceLogEvent, UUID)} method
@@ -103,7 +103,7 @@ public class RaceLogImpl extends TrackImpl<RaceLogEvent> implements RaceLog {
this.currentPassId = newPassId;
}
}
@Override
public boolean add(RaceLogEvent event) {
boolean isAdded = false;
@@ -279,12 +279,39 @@ public class RaceLogImpl extends TrackImpl<RaceLogEvent> implements RaceLog {
}
/**
* When deserializing, needs to initialize empty set of listeners.
* When deserializing, needs to initialize empty set of listeners. Furthermore, as a migration effort, when the
* {@link #eventsById} field was introduced, old clients get <code>null</code> as its value when deserializing which
* leads to NPEs later on. However, since the map is redundant to the contents of the <code>fixes</code> collection,
* it can be reconstructed here.
*/
private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException {
ois.defaultReadObject();
listeners = new HashSet<RaceLogEventVisitor>();
eventsDeliveredToClient = new HashMap<UUID, Set<RaceLogEvent>>();
if (eventsById == null) {
eventsById = new HashMap<Serializable, RaceLogEvent>();
lockForRead();
try {
for (RaceLogEvent event : getRawFixes()) {
eventsById.put(event.getId(), event);
}
} finally {
unlockAfterRead();
}
}
if (revokedEventIds == null) {
revokedEventIds = new HashSet<Serializable>();
lockForRead();
try {
for (RaceLogEvent event : getRawFixes()) {
if (event instanceof RevokeEvent) {
revokedEventIds.add(event.getId());
}
}
} finally {
unlockAfterRead();
}
}
}
@Override
@@ -0,0 +1,15 @@
package com.sap.sailing.domain.base;
import java.net.URL;
import com.sap.sailing.domain.common.Named;
/**
* Represents a {@link URL}-based reference to a remote server.
*
* @author Frank
*
*/
public interface RemoteSailingServerReference extends Named {
URL getURL();
}
@@ -0,0 +1,23 @@
package com.sap.sailing.domain.base.impl;
import java.net.URL;
import com.sap.sailing.domain.base.RemoteSailingServerReference;
import com.sap.sailing.domain.common.impl.NamedImpl;
public class RemoteSailingServerReferenceImpl extends NamedImpl implements RemoteSailingServerReference {
private static final long serialVersionUID = 3561284977118738878L;
/** the URL of the server */
private final URL url;
public RemoteSailingServerReferenceImpl(String name, URL url) {
super(name);
this.url = url;
}
@Override
public URL getURL() {
return url;
}
}
@@ -1109,7 +1109,7 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
final LeaderboardDTO result = new LeaderboardDTO(this.getScoreCorrection().getTimePointOfLastCorrectionsValidity() == null ? null
: this.getScoreCorrection().getTimePointOfLastCorrectionsValidity().asDate(),
this.getScoreCorrection() == null ? null : this.getScoreCorrection().getComment(), this
.getScoringScheme().isHigherBetter(), new UUIDGenerator());
.getScoringScheme().isHigherBetter(), new UUIDGenerator(), addOverallDetails);
result.competitors = new ArrayList<CompetitorDTO>();
result.name = this.getName();
result.competitorDisplayNames = new HashMap<CompetitorDTO, String>();
@@ -60,7 +60,7 @@ public class TestColumnSwapping {
public String generateRandomUUID() {
return UUID.randomUUID().toString();
}
});
}, /* hasOverallDetails */ false);
leaderboardOriginalDTO.addRace("Race1", /* explicitFactor */ null, 2., DEFAULT_FLEET, true, null, null);
leaderboardOriginalDTO.addRace("Race3", /* explicitFactor */ null, 2., DEFAULT_FLEET, true, null, null);
leaderboardOriginalDTO.addRace("Race2", /* explicitFactor */ null, 2., DEFAULT_FLEET, true, null, null);
@@ -123,7 +123,7 @@ public class TestColumnSwapping {
public String generateRandomUUID() {
return UUID.randomUUID().toString();
}
});
}, /* hasOverallDetails */ false);
assertNotNull("Leaderboard != NULL", lb);
lb.addRace("1", /* explicitFactor */ null, 1., DEFAULT_FLEET, false, null, null);
lb.addRace("2", /* explicitFactor */ null, 1., DEFAULT_FLEET, false, null, null);
@@ -206,7 +206,7 @@ public class AddDeviceMappingDialog extends DataEntryDialog<DeviceMappingDTO> {
return panel;
}
private static <T> void deselectAll(SelectionModel<T> selectionModel, Collection<T> list) {
private static <T> void deselectAll(SelectionModel<T> selectionModel, Iterable<T> list) {
for (T t : list) {
selectionModel.setSelected(t, false);
}
@@ -1,235 +1,238 @@
package com.sap.sailing.gwt.ui.adminconsole;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.event.logical.shared.SelectionEvent;
import com.google.gwt.event.logical.shared.SelectionHandler;
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.rpc.ServiceDefTarget;
import com.google.gwt.user.client.ui.Anchor;
import com.google.gwt.user.client.ui.DockLayoutPanel;
import com.google.gwt.user.client.ui.DockPanel;
import com.google.gwt.user.client.ui.HasHorizontalAlignment;
import com.google.gwt.user.client.ui.RootLayoutPanel;
import com.google.gwt.user.client.ui.ScrollPanel;
import com.google.gwt.user.client.ui.TabLayoutPanel;
import com.google.gwt.user.client.ui.Widget;
import com.sap.sailing.gwt.ui.client.AbstractEntryPoint;
import com.sap.sailing.gwt.ui.client.MediaService;
import com.sap.sailing.gwt.ui.client.MediaServiceAsync;
import com.sap.sailing.gwt.ui.client.RegattaDisplayer;
import com.sap.sailing.gwt.ui.client.RegattaRefresher;
import com.sap.sailing.gwt.ui.client.RemoteServiceMappingConstants;
import com.sap.sailing.gwt.ui.client.SailingService;
import com.sap.sailing.gwt.ui.client.SailingServiceAsync;
import com.sap.sailing.gwt.ui.client.UserManagementService;
import com.sap.sailing.gwt.ui.client.UserManagementServiceAsync;
import com.sap.sailing.gwt.ui.client.shared.controls.ScrolledTabLayoutPanel;
import com.sap.sailing.gwt.ui.client.shared.panels.SystemInformationPanel;
import com.sap.sailing.gwt.ui.client.shared.panels.UserStatusPanel;
import com.sap.sailing.gwt.ui.masterdataimport.MasterDataImportPanel;
import com.sap.sailing.gwt.ui.shared.BetterDateTimeBox;
import com.sap.sailing.gwt.ui.shared.RegattaDTO;
import com.sap.sse.gwt.client.async.AsyncActionsExecutor;
import com.sap.sse.gwt.client.async.MarkedAsyncCallback;
public class AdminConsoleEntryPoint extends AbstractEntryPoint implements RegattaRefresher {
private Set<RegattaDisplayer> regattaDisplayers;
private final AdminConsoleResources resources = GWT.create(AdminConsoleResources.class);
private final SailingServiceAsync sailingService = GWT.create(SailingService.class);
private final MediaServiceAsync mediaService = GWT.create(MediaService.class);
private final UserManagementServiceAsync userManagementService = GWT.create(UserManagementService.class);
@Override
protected void doOnModuleLoad() {
super.doOnModuleLoad();
registerASyncService((ServiceDefTarget) userManagementService, RemoteServiceMappingConstants.userManagementServiceRemotePath);
registerASyncService((ServiceDefTarget) sailingService, RemoteServiceMappingConstants.sailingServiceRemotePath);
registerASyncService((ServiceDefTarget) mediaService, RemoteServiceMappingConstants.mediaServiceRemotePath);
BetterDateTimeBox.initialize();
RootLayoutPanel rootPanel = RootLayoutPanel.get();
DockLayoutPanel dockPanel = new DockLayoutPanel(Unit.EM);
rootPanel.add(dockPanel);
DockPanel topInformationPanel = new DockPanel();
topInformationPanel.setSize("100%", "95%");
UserStatusPanel userStatusPanel = new UserStatusPanel(userManagementService, this);
userStatusPanel.ensureDebugId("UserStatus");
topInformationPanel.add(userStatusPanel, DockPanel.WEST);
topInformationPanel.add(persistentAlertLabel, DockPanel.CENTER);
final Anchor releaseNotesLink = new Anchor(new SafeHtmlBuilder().appendEscaped(stringMessages.releaseNotes()).toSafeHtml(), "/release_notes_admin.html");
topInformationPanel.add(releaseNotesLink, DockPanel.EAST);
topInformationPanel.setCellHorizontalAlignment(releaseNotesLink, HasHorizontalAlignment.ALIGN_RIGHT);
dockPanel.addNorth(topInformationPanel, 2.5);
TabLayoutPanel tabPanel = new ScrolledTabLayoutPanel(2.5, Unit.EM, resources.arrowLeft(), resources.arrowRight(), 200);
tabPanel.ensureDebugId("AdministrationTabs");
regattaDisplayers = new HashSet<RegattaDisplayer>();
SailingEventManagementPanel sailingEventManagementPanel = new SailingEventManagementPanel(sailingService, this,
stringMessages);
sailingEventManagementPanel.setSize("90%", "90%");
addScrollableTab(tabPanel, sailingEventManagementPanel, stringMessages.events());
RegattaStructureManagementPanel eventStructureManagementPanel = new RegattaStructureManagementPanel(
sailingService, this, stringMessages, this);
eventStructureManagementPanel.ensureDebugId("RegattaStructureManagement");
eventStructureManagementPanel.setSize("90%", "90%");
addScrollableTab(tabPanel, eventStructureManagementPanel, stringMessages.regattas());
regattaDisplayers.add(eventStructureManagementPanel);
TracTracEventManagementPanel tractracEventManagementPanel = new TracTracEventManagementPanel(sailingService,
this, this, stringMessages);
tractracEventManagementPanel.ensureDebugId("TracTracEventManagement");
tractracEventManagementPanel.setSize("90%", "90%");
addScrollableTab(tabPanel, tractracEventManagementPanel, stringMessages.tracTracEvents());
regattaDisplayers.add(tractracEventManagementPanel);
SwissTimingReplayConnectorPanel swissTimingReplayConnectorPanel = new SwissTimingReplayConnectorPanel(
sailingService, this, this, stringMessages);
swissTimingReplayConnectorPanel.setSize("90%", "90%");
addScrollableTab(tabPanel, swissTimingReplayConnectorPanel, stringMessages.swissTimingArchiveConnector());
regattaDisplayers.add(swissTimingReplayConnectorPanel);
SwissTimingEventManagementPanel swisstimingEventManagementPanel = new SwissTimingEventManagementPanel(
sailingService, this, this, stringMessages);
// swisstimingEventManagementPanel.ensureDebugId("SwissTimingEventManagement");
swisstimingEventManagementPanel.setSize("90%", "90%");
addScrollableTab(tabPanel, swisstimingEventManagementPanel, stringMessages.swissTimingEvents());
regattaDisplayers.add(swisstimingEventManagementPanel);
package com.sap.sailing.gwt.ui.adminconsole;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.event.logical.shared.SelectionEvent;
import com.google.gwt.event.logical.shared.SelectionHandler;
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.rpc.ServiceDefTarget;
import com.google.gwt.user.client.ui.Anchor;
import com.google.gwt.user.client.ui.DockLayoutPanel;
import com.google.gwt.user.client.ui.DockPanel;
import com.google.gwt.user.client.ui.HasHorizontalAlignment;
import com.google.gwt.user.client.ui.RootLayoutPanel;
import com.google.gwt.user.client.ui.ScrollPanel;
import com.google.gwt.user.client.ui.TabLayoutPanel;
import com.google.gwt.user.client.ui.Widget;
import com.sap.sailing.gwt.ui.client.AbstractEntryPoint;
import com.sap.sailing.gwt.ui.client.MediaService;
import com.sap.sailing.gwt.ui.client.MediaServiceAsync;
import com.sap.sailing.gwt.ui.client.RegattaDisplayer;
import com.sap.sailing.gwt.ui.client.RegattaRefresher;
import com.sap.sailing.gwt.ui.client.RemoteServiceMappingConstants;
import com.sap.sailing.gwt.ui.client.SailingService;
import com.sap.sailing.gwt.ui.client.SailingServiceAsync;
import com.sap.sailing.gwt.ui.client.UserManagementService;
import com.sap.sailing.gwt.ui.client.UserManagementServiceAsync;
import com.sap.sailing.gwt.ui.client.shared.controls.ScrolledTabLayoutPanel;
import com.sap.sailing.gwt.ui.client.shared.panels.SystemInformationPanel;
import com.sap.sailing.gwt.ui.client.shared.panels.UserStatusPanel;
import com.sap.sailing.gwt.ui.masterdataimport.MasterDataImportPanel;
import com.sap.sailing.gwt.ui.shared.BetterDateTimeBox;
import com.sap.sailing.gwt.ui.shared.RegattaDTO;
import com.sap.sse.gwt.client.async.AsyncActionsExecutor;
import com.sap.sse.gwt.client.async.MarkedAsyncCallback;
public class AdminConsoleEntryPoint extends AbstractEntryPoint implements RegattaRefresher {
private Set<RegattaDisplayer> regattaDisplayers;
private final AdminConsoleResources resources = GWT.create(AdminConsoleResources.class);
private final SailingServiceAsync sailingService = GWT.create(SailingService.class);
private final MediaServiceAsync mediaService = GWT.create(MediaService.class);
private final UserManagementServiceAsync userManagementService = GWT.create(UserManagementService.class);
@Override
protected void doOnModuleLoad() {
super.doOnModuleLoad();
registerASyncService((ServiceDefTarget) userManagementService, RemoteServiceMappingConstants.userManagementServiceRemotePath);
registerASyncService((ServiceDefTarget) sailingService, RemoteServiceMappingConstants.sailingServiceRemotePath);
registerASyncService((ServiceDefTarget) mediaService, RemoteServiceMappingConstants.mediaServiceRemotePath);
BetterDateTimeBox.initialize();
RootLayoutPanel rootPanel = RootLayoutPanel.get();
DockLayoutPanel dockPanel = new DockLayoutPanel(Unit.EM);
rootPanel.add(dockPanel);
DockPanel topInformationPanel = new DockPanel();
topInformationPanel.setSize("100%", "95%");
UserStatusPanel userStatusPanel = new UserStatusPanel(userManagementService, this);
userStatusPanel.ensureDebugId("UserStatus");
topInformationPanel.add(userStatusPanel, DockPanel.WEST);
topInformationPanel.add(persistentAlertLabel, DockPanel.CENTER);
final Anchor releaseNotesLink = new Anchor(new SafeHtmlBuilder().appendEscaped(stringMessages.releaseNotes()).toSafeHtml(), "/release_notes_admin.html");
topInformationPanel.add(releaseNotesLink, DockPanel.EAST);
topInformationPanel.setCellHorizontalAlignment(releaseNotesLink, HasHorizontalAlignment.ALIGN_RIGHT);
dockPanel.addNorth(topInformationPanel, 2.5);
TabLayoutPanel tabPanel = new ScrolledTabLayoutPanel(2.5, Unit.EM, resources.arrowLeft(), resources.arrowRight(), 200);
tabPanel.ensureDebugId("AdministrationTabs");
regattaDisplayers = new HashSet<RegattaDisplayer>();
SailingEventManagementPanel sailingEventManagementPanel = new SailingEventManagementPanel(sailingService, this,
stringMessages);
sailingEventManagementPanel.setSize("90%", "90%");
addScrollableTab(tabPanel, sailingEventManagementPanel, stringMessages.events());
RegattaStructureManagementPanel eventStructureManagementPanel = new RegattaStructureManagementPanel(
sailingService, this, stringMessages, this);
eventStructureManagementPanel.ensureDebugId("RegattaStructureManagement");
eventStructureManagementPanel.setSize("90%", "90%");
addScrollableTab(tabPanel, eventStructureManagementPanel, stringMessages.regattas());
regattaDisplayers.add(eventStructureManagementPanel);
TracTracEventManagementPanel tractracEventManagementPanel = new TracTracEventManagementPanel(sailingService,
this, this, stringMessages);
tractracEventManagementPanel.ensureDebugId("TracTracEventManagement");
tractracEventManagementPanel.setSize("90%", "90%");
addScrollableTab(tabPanel, tractracEventManagementPanel, stringMessages.tracTracEvents());
regattaDisplayers.add(tractracEventManagementPanel);
SwissTimingReplayConnectorPanel swissTimingReplayConnectorPanel = new SwissTimingReplayConnectorPanel(
sailingService, this, this, stringMessages);
swissTimingReplayConnectorPanel.setSize("90%", "90%");
addScrollableTab(tabPanel, swissTimingReplayConnectorPanel, stringMessages.swissTimingArchiveConnector());
regattaDisplayers.add(swissTimingReplayConnectorPanel);
SwissTimingEventManagementPanel swisstimingEventManagementPanel = new SwissTimingEventManagementPanel(
sailingService, this, this, stringMessages);
// swisstimingEventManagementPanel.ensureDebugId("SwissTimingEventManagement");
swisstimingEventManagementPanel.setSize("90%", "90%");
addScrollableTab(tabPanel, swisstimingEventManagementPanel, stringMessages.swissTimingEvents());
regattaDisplayers.add(swisstimingEventManagementPanel);
final RaceLogTrackingEventManagementPanel raceLogTrackingEventManagementPanel = new RaceLogTrackingEventManagementPanel(
sailingService, this, this, this, stringMessages);
raceLogTrackingEventManagementPanel.setSize("90%", "90%");
addScrollableTab(tabPanel, raceLogTrackingEventManagementPanel, stringMessages.raceLogTracking());
regattaDisplayers.add(raceLogTrackingEventManagementPanel);
IgtimiAccountsPanel igtimiAccountsPanel = new IgtimiAccountsPanel(sailingService, this, stringMessages);
igtimiAccountsPanel.ensureDebugId("IgtimiAccounts");
igtimiAccountsPanel.setSize("90%", "90%");
addScrollableTab(tabPanel, igtimiAccountsPanel, stringMessages.igtimiAccounts());
TrackedRacesManagementPanel trackedRacesManagementPanel = new TrackedRacesManagementPanel(sailingService, this,
this, stringMessages);
trackedRacesManagementPanel.ensureDebugId("TrackedRacesManagement");
trackedRacesManagementPanel.setSize("90%", "90%");
addScrollableTab(tabPanel, trackedRacesManagementPanel, stringMessages.trackedRaces());
regattaDisplayers.add(trackedRacesManagementPanel);
final CompetitorPanel competitorPanel = new CompetitorPanel(sailingService, stringMessages, this);
// trackedRacesManagementPanel.ensureDebugId("TrackedRacesManagement");
trackedRacesManagementPanel.setSize("90%", "90%");
addScrollableTab(tabPanel, competitorPanel, stringMessages.competitors());
RaceCourseManagementPanel raceCourseManagementPanel = new RaceCourseManagementPanel(sailingService, this, this,
stringMessages);
// raceCourseManagementPanel.ensureDebugId("RaceCourseManagement");
raceCourseManagementPanel.setSize("90%", "90%");
addScrollableTab(tabPanel, raceCourseManagementPanel, stringMessages.courseLayout());
regattaDisplayers.add(raceCourseManagementPanel);
final AsyncActionsExecutor asyncActionsExecutor = new AsyncActionsExecutor();
WindPanel windPanel = new WindPanel(sailingService, asyncActionsExecutor, this, this, stringMessages);
// windPanel.ensureDebugId("WindManagement");
regattaDisplayers.add(windPanel);
windPanel.setSize("90%", "90%");
addScrollableTab(tabPanel, windPanel, stringMessages.wind());
final LeaderboardGroupConfigPanel leaderboardGroupConfigPanel = new LeaderboardGroupConfigPanel(sailingService,
this, this, stringMessages);
leaderboardGroupConfigPanel.ensureDebugId("LeaderboardGroupConfiguration");
leaderboardGroupConfigPanel.setSize("90%", "90%");
addScrollableTab(tabPanel, leaderboardGroupConfigPanel, stringMessages.leaderboardGroupConfiguration());
regattaDisplayers.add(leaderboardGroupConfigPanel);
final LeaderboardConfigPanel leaderboardConfigPanel = new LeaderboardConfigPanel(sailingService, this, this,
stringMessages,
/* showRaceDetails */true);
leaderboardConfigPanel.ensureDebugId("LeaderboardConfiguration");
leaderboardConfigPanel.setSize("90%", "90%");
addScrollableTab(tabPanel, leaderboardConfigPanel, stringMessages.leaderboardConfiguration());
regattaDisplayers.add(leaderboardConfigPanel);
addScrollableTab(tabPanel, new ResultImportUrlsManagementPanel(sailingService, this, stringMessages),
stringMessages.resultImportUrls());
ReplicationPanel replicationPanel = new ReplicationPanel(sailingService, this, stringMessages);
// replicationPanel.ensureDebugId("ReplicationManagement");
addScrollableTab(tabPanel, replicationPanel, stringMessages.replication());
final MediaPanel mediaPanel = new MediaPanel(mediaService, this, stringMessages);
addScrollableTab(tabPanel, mediaPanel, stringMessages.mediaPanel());
final MasterDataImportPanel masterDataImportPanel = new MasterDataImportPanel(stringMessages, sailingService,
this, sailingEventManagementPanel, leaderboardGroupConfigPanel);
addScrollableTab(tabPanel, masterDataImportPanel, stringMessages.masterDataImportPanel());
/*final DeviceConfigurationPanel deviceConfigurationAdminPanel = new DeviceConfigurationPanel(sailingService,
stringMessages, this);
addScrollableTab(tabPanel, deviceConfigurationAdminPanel, stringMessages.deviceConfiguration() + " (admin)");*/
final DeviceConfigurationPanel deviceConfigurationUserPanel = new DeviceConfigurationUserPanel(sailingService,
stringMessages, this);
addScrollableTab(tabPanel, deviceConfigurationUserPanel, stringMessages.deviceConfiguration());
tabPanel.selectTab(0);
tabPanel.addSelectionHandler(new SelectionHandler<Integer>() {
@Override
public void onSelection(SelectionEvent<Integer> event) {
if (leaderboardConfigPanel.isVisible()) {
leaderboardConfigPanel.loadLeaderboards();
}
if (mediaPanel.isVisible()) {
mediaPanel.onShow();
}
if (competitorPanel.isVisible()) {
competitorPanel.refreshCompetitorList();
}
IgtimiAccountsPanel igtimiAccountsPanel = new IgtimiAccountsPanel(sailingService, this, stringMessages);
igtimiAccountsPanel.ensureDebugId("IgtimiAccounts");
igtimiAccountsPanel.setSize("90%", "90%");
addScrollableTab(tabPanel, igtimiAccountsPanel, stringMessages.igtimiAccounts());
TrackedRacesManagementPanel trackedRacesManagementPanel = new TrackedRacesManagementPanel(sailingService, this,
this, stringMessages);
trackedRacesManagementPanel.ensureDebugId("TrackedRacesManagement");
trackedRacesManagementPanel.setSize("90%", "90%");
addScrollableTab(tabPanel, trackedRacesManagementPanel, stringMessages.trackedRaces());
regattaDisplayers.add(trackedRacesManagementPanel);
final CompetitorPanel competitorPanel = new CompetitorPanel(sailingService, stringMessages, this);
// trackedRacesManagementPanel.ensureDebugId("TrackedRacesManagement");
trackedRacesManagementPanel.setSize("90%", "90%");
addScrollableTab(tabPanel, competitorPanel, stringMessages.competitors());
RaceCourseManagementPanel raceCourseManagementPanel = new RaceCourseManagementPanel(sailingService, this, this,
stringMessages);
// raceCourseManagementPanel.ensureDebugId("RaceCourseManagement");
raceCourseManagementPanel.setSize("90%", "90%");
addScrollableTab(tabPanel, raceCourseManagementPanel, stringMessages.courseLayout());
regattaDisplayers.add(raceCourseManagementPanel);
final AsyncActionsExecutor asyncActionsExecutor = new AsyncActionsExecutor();
WindPanel windPanel = new WindPanel(sailingService, asyncActionsExecutor, this, this, stringMessages);
// windPanel.ensureDebugId("WindManagement");
regattaDisplayers.add(windPanel);
windPanel.setSize("90%", "90%");
addScrollableTab(tabPanel, windPanel, stringMessages.wind());
final LeaderboardGroupConfigPanel leaderboardGroupConfigPanel = new LeaderboardGroupConfigPanel(sailingService,
this, this, stringMessages);
leaderboardGroupConfigPanel.ensureDebugId("LeaderboardGroupConfiguration");
leaderboardGroupConfigPanel.setSize("90%", "90%");
addScrollableTab(tabPanel, leaderboardGroupConfigPanel, stringMessages.leaderboardGroupConfiguration());
regattaDisplayers.add(leaderboardGroupConfigPanel);
final LeaderboardConfigPanel leaderboardConfigPanel = new LeaderboardConfigPanel(sailingService, this, this,
stringMessages,
/* showRaceDetails */true);
leaderboardConfigPanel.ensureDebugId("LeaderboardConfiguration");
leaderboardConfigPanel.setSize("90%", "90%");
addScrollableTab(tabPanel, leaderboardConfigPanel, stringMessages.leaderboardConfiguration());
regattaDisplayers.add(leaderboardConfigPanel);
ResultImportUrlsManagementPanel resultImportUrlsManagementPanel = new ResultImportUrlsManagementPanel(sailingService, this, stringMessages);
addScrollableTab(tabPanel, resultImportUrlsManagementPanel, stringMessages.resultImportUrls());
RemoteSailingServerInstancesManagementPanel sailingServerInstancesManagementPanel = new RemoteSailingServerInstancesManagementPanel(sailingService, this, stringMessages);
addScrollableTab(tabPanel, sailingServerInstancesManagementPanel, stringMessages.sailingServers());
ReplicationPanel replicationPanel = new ReplicationPanel(sailingService, this, stringMessages);
// replicationPanel.ensureDebugId("ReplicationManagement");
addScrollableTab(tabPanel, replicationPanel, stringMessages.replication());
final MediaPanel mediaPanel = new MediaPanel(mediaService, this, stringMessages);
addScrollableTab(tabPanel, mediaPanel, stringMessages.mediaPanel());
final MasterDataImportPanel masterDataImportPanel = new MasterDataImportPanel(stringMessages, sailingService,
this, sailingEventManagementPanel, leaderboardGroupConfigPanel);
addScrollableTab(tabPanel, masterDataImportPanel, stringMessages.masterDataImportPanel());
/*final DeviceConfigurationPanel deviceConfigurationAdminPanel = new DeviceConfigurationPanel(sailingService,
stringMessages, this);
addScrollableTab(tabPanel, deviceConfigurationAdminPanel, stringMessages.deviceConfiguration() + " (admin)");*/
final DeviceConfigurationPanel deviceConfigurationUserPanel = new DeviceConfigurationUserPanel(sailingService,
stringMessages, this);
addScrollableTab(tabPanel, deviceConfigurationUserPanel, stringMessages.deviceConfiguration());
tabPanel.selectTab(0);
tabPanel.addSelectionHandler(new SelectionHandler<Integer>() {
@Override
public void onSelection(SelectionEvent<Integer> event) {
if (leaderboardConfigPanel.isVisible()) {
leaderboardConfigPanel.loadLeaderboards();
}
if (mediaPanel.isVisible()) {
mediaPanel.onShow();
}
if (competitorPanel.isVisible()) {
competitorPanel.refreshCompetitorList();
}
if (raceLogTrackingEventManagementPanel.isVisible()) {
raceLogTrackingEventManagementPanel.loadLeaderboards();
}
}
});
fillRegattas();
SystemInformationPanel sysinfoPanel = new SystemInformationPanel(sailingService, this);
sysinfoPanel.ensureDebugId("SystemInformation");
dockPanel.addSouth(sysinfoPanel, 2.0);
dockPanel.add(tabPanel);
}
private void addScrollableTab(TabLayoutPanel tabPanel, Widget widget, String tabTitle) {
ScrollPanel widgetScroller = new ScrollPanel();
widgetScroller.add(widget);
tabPanel.add(widgetScroller, tabTitle, false);
}
@Override
public void fillRegattas() {
sailingService.getRegattas(new MarkedAsyncCallback<List<RegattaDTO>>(
new AsyncCallback<List<RegattaDTO>>() {
@Override
public void onSuccess(List<RegattaDTO> result) {
for (RegattaDisplayer regattaDisplayer : regattaDisplayers) {
regattaDisplayer.fillRegattas(result);
}
}
@Override
public void onFailure(Throwable caught) {
reportError("Remote Procedure Call getRegattas() - Failure");
}
}));
}
}
}
});
fillRegattas();
SystemInformationPanel sysinfoPanel = new SystemInformationPanel(sailingService, this);
sysinfoPanel.ensureDebugId("SystemInformation");
dockPanel.addSouth(sysinfoPanel, 2.0);
dockPanel.add(tabPanel);
}
private void addScrollableTab(TabLayoutPanel tabPanel, Widget widget, String tabTitle) {
ScrollPanel widgetScroller = new ScrollPanel();
widgetScroller.add(widget);
tabPanel.add(widgetScroller, tabTitle, false);
}
@Override
public void fillRegattas() {
sailingService.getRegattas(new MarkedAsyncCallback<List<RegattaDTO>>(
new AsyncCallback<List<RegattaDTO>>() {
@Override
public void onSuccess(List<RegattaDTO> result) {
for (RegattaDisplayer regattaDisplayer : regattaDisplayers) {
regattaDisplayer.fillRegattas(result);
}
}
@Override
public void onFailure(Throwable caught) {
reportError("Remote Procedure Call getRegattas() - Failure");
}
}));
}
}
@@ -1,7 +1,6 @@
package com.sap.sailing.gwt.ui.adminconsole;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.List;
@@ -141,7 +140,7 @@ public class CompetitorTableWrapper<S extends SelectionModel<CompetitorDTO>> ext
mainPanel.add(table);
}
public Collection<CompetitorDTO> getAllCompetitors() {
public Iterable<CompetitorDTO> getAllCompetitors() {
return filterField.getAll();
}
@@ -19,6 +19,7 @@ import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.view.client.MultiSelectionModel;
import com.sap.sailing.domain.common.dto.CompetitorDTO;
import com.sap.sailing.domain.common.dto.CompetitorDTOImpl;
import com.sap.sailing.domain.common.impl.Util;
import com.sap.sailing.gwt.ui.client.ErrorReporter;
import com.sap.sailing.gwt.ui.client.SailingServiceAsync;
import com.sap.sailing.gwt.ui.client.StringMessages;
@@ -57,10 +58,12 @@ public class RaceLogTrackingCompetitorRegistrationsDialog extends RaceLogTrackin
if (toMove.isEmpty()) {
return;
}
List<CompetitorDTO> newFromList = new ArrayList<>(from.getFilterField().getAll());
List<CompetitorDTO> newFromList = new ArrayList<>();
Util.addAll(from.getFilterField().getAll(), newFromList);
newFromList.removeAll(toMove);
from.getFilterField().updateAll(newFromList);
List<CompetitorDTO> newToList = new ArrayList<>(to.getFilterField().getAll());
List<CompetitorDTO> newToList = new ArrayList<>();
Util.addAll(to.getFilterField().getAll(), newToList);
newToList.addAll(toMove);
to.getFilterField().updateAll(newToList);
}
@@ -111,8 +114,8 @@ public class RaceLogTrackingCompetitorRegistrationsDialog extends RaceLogTrackin
@Override
protected void save() {
final Set<CompetitorDTO> registeredCompetitors = new HashSet<>(
registeredCompetitorsTable.getAllCompetitors());
final Set<CompetitorDTO> registeredCompetitors = new HashSet<>();
Util.addAll(registeredCompetitorsTable.getAllCompetitors(), registeredCompetitors);
sailingService.setCompetitorRegistrations(leaderboardName, raceColumnName, fleetName,
registeredCompetitors, new AsyncCallback<Void>() {
@Override
@@ -0,0 +1,205 @@
package com.sap.sailing.gwt.ui.adminconsole;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import com.google.gwt.cell.client.SafeHtmlCell;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.safehtml.shared.SafeHtml;
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
import com.google.gwt.user.cellview.client.CellTable;
import com.google.gwt.user.cellview.client.Column;
import com.google.gwt.user.cellview.client.TextColumn;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.HasVerticalAlignment;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.view.client.ListDataProvider;
import com.google.gwt.view.client.MultiSelectionModel;
import com.sap.sailing.gwt.ui.client.ErrorReporter;
import com.sap.sailing.gwt.ui.client.SailingServiceAsync;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.client.shared.panels.LabeledAbstractFilterablePanel;
import com.sap.sailing.gwt.ui.shared.EventDTO;
import com.sap.sailing.gwt.ui.shared.RemoteSailingServerReferenceDTO;
import com.sap.sse.gwt.client.dialog.DataEntryDialog.DialogCallback;
public class RemoteSailingServerInstancesManagementPanel extends FlowPanel {
private final SailingServiceAsync sailingService;
private final ErrorReporter errorReporter;
private final StringMessages stringMessages;
private final AdminConsoleTableResources tableRes = GWT.create(AdminConsoleTableResources.class);
private final MultiSelectionModel<RemoteSailingServerReferenceDTO> serverSelectionModel;
private LabeledAbstractFilterablePanel<RemoteSailingServerReferenceDTO> filteredServerTable;
public RemoteSailingServerInstancesManagementPanel(SailingServiceAsync sailingService, ErrorReporter errorReporter,
StringMessages stringMessages) {
this.sailingService = sailingService;
this.errorReporter = errorReporter;
this.stringMessages = stringMessages;
TextColumn<RemoteSailingServerReferenceDTO> serverNameColumn = new TextColumn<RemoteSailingServerReferenceDTO>() {
@Override
public String getValue(RemoteSailingServerReferenceDTO server) {
return server.getName() != null ? server.getName() : "";
}
};
TextColumn<RemoteSailingServerReferenceDTO> serverUrlColumn = new TextColumn<RemoteSailingServerReferenceDTO>() {
@Override
public String getValue(RemoteSailingServerReferenceDTO server) {
return server.getUrl() != null ? server.getUrl() : "";
}
};
final SafeHtmlCell eventsCell = new SafeHtmlCell();
Column<RemoteSailingServerReferenceDTO, SafeHtml> eventsOrErrorColumn = new Column<RemoteSailingServerReferenceDTO, SafeHtml>(eventsCell) {
@Override
public SafeHtml getValue(RemoteSailingServerReferenceDTO server) {
SafeHtmlBuilder builder = new SafeHtmlBuilder();
final Iterable<EventDTO> events = server.getEvents();
if (events != null) {
for (EventDTO event : events) {
builder.appendEscaped(event.getName());
builder.appendHtmlConstant("<br>");
}
} else {
builder.appendEscaped(RemoteSailingServerInstancesManagementPanel.this.stringMessages
.errorAddingSailingServer(server.getLastErrorMessage()));
}
return builder.toSafeHtml();
}
};
CellTable<RemoteSailingServerReferenceDTO> serverTable = new CellTable<RemoteSailingServerReferenceDTO>(10000, tableRes);
serverTable.addColumn(serverNameColumn, stringMessages.name());
serverTable.addColumn(serverUrlColumn, stringMessages.url());
serverTable.addColumn(eventsOrErrorColumn, stringMessages.events());
serverTable.setEmptyTableWidget(new Label(stringMessages.noSailingServerInstancesYet()));
serverSelectionModel = new MultiSelectionModel<RemoteSailingServerReferenceDTO>();
serverTable.setSelectionModel(serverSelectionModel);
ListDataProvider<RemoteSailingServerReferenceDTO> serverDataProvider = new ListDataProvider<RemoteSailingServerReferenceDTO>();
serverDataProvider.addDataDisplay(serverTable);
Button addButton = new Button(stringMessages.add());
Button removeButton = new Button(stringMessages.remove());
Button refreshButton = new Button(stringMessages.refresh());
addButton.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
addRemoteSailingServerReference();
}
});
removeButton.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
removeSelectedSailingServers();
}
});
refreshButton.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
refreshSailingServerList();
}
});
VerticalPanel vp = new VerticalPanel();
HorizontalPanel providerSelectionPanel = new HorizontalPanel();
providerSelectionPanel.setVerticalAlignment(HasVerticalAlignment.ALIGN_MIDDLE);
vp.add(providerSelectionPanel);
filteredServerTable = new LabeledAbstractFilterablePanel<RemoteSailingServerReferenceDTO>(
new Label(stringMessages.registeredSailingServerInstances()), Collections.<RemoteSailingServerReferenceDTO>emptyList(), serverTable, serverDataProvider) {
@Override
public List<String> getSearchableStrings(RemoteSailingServerReferenceDTO t) {
List<String> strings = new ArrayList<String>();
strings.add(t.getName());
strings.add(t.getUrl());
if (t.getEvents() != null) {
for (EventDTO e : t.getEvents()) {
strings.add(e.getName());
}
}
return strings;
}
};
providerSelectionPanel.add(filteredServerTable);
vp.add(serverTable);
HorizontalPanel buttonPanel = new HorizontalPanel();
buttonPanel.add(addButton);
buttonPanel.add(removeButton);
buttonPanel.add(refreshButton);
vp.add(buttonPanel);
add(vp);
refreshSailingServerList();
}
private void refreshSailingServerList() {
sailingService.getRemoteSailingServerReferences(new AsyncCallback<List<RemoteSailingServerReferenceDTO>>() {
@Override
public void onFailure(Throwable caught) {
errorReporter.reportError(stringMessages.errorRefreshingSailingServers(caught.getMessage()));
}
@Override
public void onSuccess(List<RemoteSailingServerReferenceDTO> result) {
filteredServerTable.updateAll(result);
}
});
}
private void removeSelectedSailingServers() {
Set<String> toRemove = new HashSet<String>();
for (RemoteSailingServerReferenceDTO selectedServer: serverSelectionModel.getSelectedSet()) {
toRemove.add(selectedServer.getName());
}
sailingService.removeSailingServers(toRemove, new AsyncCallback<Void>() {
@Override
public void onFailure(Throwable caught) {
errorReporter.reportError(stringMessages.errorRemovingSailingServers(caught.getMessage()));
}
@Override
public void onSuccess(Void result) {
refreshSailingServerList();
Window.setStatus(stringMessages.successfullyUpdatedSailingServers());
}
});
}
private void addRemoteSailingServerReference() {
SailingServerCreateOrEditDialog dialog = new SailingServerCreateOrEditDialog(filteredServerTable.getAll(), stringMessages, new DialogCallback<RemoteSailingServerReferenceDTO>() {
@Override
public void cancel() {
}
@Override
public void ok(final RemoteSailingServerReferenceDTO server) {
sailingService.addRemoteSailingServerReference(server, new AsyncCallback<RemoteSailingServerReferenceDTO>() {
@Override
public void onFailure(Throwable caught) {
errorReporter.reportError(stringMessages.errorAddingSailingServer(caught.getMessage()));
}
@Override
public void onSuccess(RemoteSailingServerReferenceDTO result) {
filteredServerTable.add(result);
Window.setStatus(stringMessages.successfullyUpdatedSailingServers());
}
});
}
});
dialog.show();
}
}
@@ -0,0 +1,102 @@
package com.sap.sailing.gwt.ui.adminconsole;
import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.shared.RemoteSailingServerReferenceDTO;
import com.sap.sse.gwt.client.dialog.DataEntryDialog;
public class SailingServerCreateOrEditDialog extends DataEntryDialog<RemoteSailingServerReferenceDTO> {
private StringMessages stringConstants;
private TextBox nameTextBox;
private TextBox urlTextBox;
private static class SailingServerValidator implements Validator<RemoteSailingServerReferenceDTO> {
private StringMessages stringConstants;
private Iterable<RemoteSailingServerReferenceDTO> existingSailingServers;
public SailingServerValidator(StringMessages stringConstants, Iterable<RemoteSailingServerReferenceDTO> existingSailingServers) {
this.stringConstants = stringConstants;
this.existingSailingServers = existingSailingServers;
}
@Override
public String getErrorMessage(RemoteSailingServerReferenceDTO serverToValidate) {
String errorMessage = null;
boolean nameNotEmpty = serverToValidate.getName() != null && serverToValidate.getName().length() > 0;
boolean urlNotEmpty = serverToValidate.getUrl() != null && serverToValidate.getUrl().length() > 0;
boolean unique = true;
for (RemoteSailingServerReferenceDTO event : existingSailingServers) {
if (event.getName().equals(serverToValidate.getName())) {
unique = false;
break;
}
}
if (!nameNotEmpty) {
errorMessage = stringConstants.pleaseEnterAName();
} else if (!urlNotEmpty) {
errorMessage = stringConstants.pleaseEnterNonEmptyUrl();
} else if (!unique) {
errorMessage = stringConstants.eventWithThisNameAlreadyExists();
}
return errorMessage;
}
}
public SailingServerCreateOrEditDialog(Iterable<RemoteSailingServerReferenceDTO> existingSailingServers,
StringMessages stringConstants, DialogCallback<RemoteSailingServerReferenceDTO> callback) {
this(existingSailingServers, null, false, stringConstants, callback);
}
public SailingServerCreateOrEditDialog(Iterable<RemoteSailingServerReferenceDTO> existingSailingServers, RemoteSailingServerReferenceDTO serverToEdit, StringMessages stringConstants,
DialogCallback<RemoteSailingServerReferenceDTO> callback) {
this(existingSailingServers, serverToEdit, true, stringConstants, callback);
}
private SailingServerCreateOrEditDialog(Iterable<RemoteSailingServerReferenceDTO> existingSailingServers, RemoteSailingServerReferenceDTO serverToEdit, boolean isEditMode, StringMessages stringConstants,
DialogCallback<RemoteSailingServerReferenceDTO> callback) {
super("Sailing Server", null, stringConstants.ok(), stringConstants.cancel(),
new SailingServerValidator(stringConstants, existingSailingServers), callback);
this.stringConstants = stringConstants;
nameTextBox = createTextBox(isEditMode ? serverToEdit.getName() : null);
nameTextBox.setVisibleLength(50);
urlTextBox = createTextBox(isEditMode ? serverToEdit.getUrl() : null);
urlTextBox.setVisibleLength(100);
nameTextBox.setEnabled(!isEditMode);
}
@Override
protected RemoteSailingServerReferenceDTO getResult() {
return new RemoteSailingServerReferenceDTO(nameTextBox.getText(), urlTextBox.getText());
}
@Override
protected Widget getAdditionalWidget() {
final VerticalPanel panel = new VerticalPanel();
Widget additionalWidget = super.getAdditionalWidget();
if (additionalWidget != null) {
panel.add(additionalWidget);
}
Grid formGrid = new Grid(2, 2);
panel.add(formGrid);
formGrid.setWidget(0, 0, new Label(stringConstants.name() + ":"));
formGrid.setWidget(0, 1, nameTextBox);
formGrid.setWidget(1, 0, new Label(stringConstants.url() + ":"));
formGrid.setWidget(1, 1, urlTextBox);
return panel;
}
@Override
public void show() {
super.show();
nameTextBox.setFocus(true);
}
}
@@ -1,372 +1,381 @@
package com.sap.sailing.gwt.ui.client;
import java.io.Serializable;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import com.google.gwt.user.client.rpc.RemoteService;
import com.sap.sailing.domain.base.RaceDefinition;
import com.sap.sailing.domain.common.DataImportProgress;
import com.sap.sailing.domain.common.DetailType;
import com.sap.sailing.domain.common.LeaderboardType;
import com.sap.sailing.domain.common.MaxPointsReason;
import com.sap.sailing.domain.common.NoWindException;
import com.sap.sailing.domain.common.PassingInstruction;
import com.sap.sailing.domain.common.PolarSheetGenerationResponse;
import com.sap.sailing.domain.common.PolarSheetGenerationSettings;
import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
import com.sap.sailing.domain.common.RegattaIdentifier;
import com.sap.sailing.domain.common.ScoringSchemeType;
import com.sap.sailing.domain.common.WindSource;
import com.sap.sailing.domain.common.configuration.DeviceConfigurationMatcherType;
import com.sap.sailing.domain.common.dto.CompetitorDTO;
import com.sap.sailing.domain.common.dto.FleetDTO;
import com.sap.sailing.domain.common.dto.IncrementalOrFullLeaderboardDTO;
import com.sap.sailing.domain.common.dto.PositionDTO;
import com.sap.sailing.domain.common.dto.RaceColumnDTO;
import com.sap.sailing.domain.common.dto.RaceColumnInSeriesDTO;
import com.sap.sailing.domain.common.dto.RaceDTO;
import com.sap.sailing.domain.common.dto.RegattaCreationParametersDTO;
import com.sap.sailing.domain.common.impl.Util.Pair;
import com.sap.sailing.domain.common.impl.Util.Triple;
import com.sap.sailing.domain.common.racelog.RacingProcedureType;
import com.sap.sailing.domain.common.racelog.tracking.NoCorrespondingServiceRegisteredException;
import com.sap.sailing.domain.common.racelog.tracking.NotDenotedForRaceLogTrackingException;
import com.sap.sailing.domain.common.racelog.tracking.NotRevokableException;
import com.sap.sailing.domain.common.racelog.tracking.TransformationException;
import com.sap.sailing.domain.racelog.RaceLog;
import com.sap.sailing.domain.racelog.Revokable;
import com.sap.sailing.domain.racelog.tracking.DenoteForTrackingEvent;
import com.sap.sailing.domain.racelog.tracking.GPSFixStore;
import com.sap.sailing.domain.racelogtracking.RaceLogTrackingAdapter;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.gwt.ui.shared.BulkScoreCorrectionDTO;
import com.sap.sailing.gwt.ui.shared.CompactRaceMapDataDTO;
import com.sap.sailing.gwt.ui.shared.CompetitorsRaceDataDTO;
import com.sap.sailing.gwt.ui.shared.ControlPointDTO;
import com.sap.sailing.gwt.ui.shared.CourseAreaDTO;
import com.sap.sailing.gwt.ui.shared.CoursePositionsDTO;
import com.sap.sailing.gwt.ui.shared.DeviceConfigurationDTO;
import com.sap.sailing.gwt.ui.shared.DeviceConfigurationDTO.RegattaConfigurationDTO;
import com.sap.sailing.gwt.ui.shared.DeviceConfigurationMatcherDTO;
import com.sap.sailing.gwt.ui.shared.DeviceMappingDTO;
import com.sap.sailing.gwt.ui.shared.EventDTO;
import com.sap.sailing.gwt.ui.shared.GPSFixDTO;
import com.sap.sailing.gwt.ui.shared.LeaderboardGroupDTO;
import com.sap.sailing.gwt.ui.shared.ManeuverDTO;
import com.sap.sailing.gwt.ui.shared.MarkDTO;
import com.sap.sailing.gwt.ui.shared.RaceCourseDTO;
import com.sap.sailing.gwt.ui.shared.RaceGroupDTO;
import com.sap.sailing.gwt.ui.shared.RaceLogDTO;
import com.sap.sailing.gwt.ui.shared.RaceLogSetStartTimeAndProcedureDTO;
import com.sap.sailing.gwt.ui.shared.RaceTimesInfoDTO;
import com.sap.sailing.gwt.ui.shared.RegattaDTO;
import com.sap.sailing.gwt.ui.shared.RegattaOverviewEntryDTO;
import com.sap.sailing.gwt.ui.shared.RegattaScoreCorrectionDTO;
import com.sap.sailing.gwt.ui.shared.ReplicationStateDTO;
import com.sap.sailing.gwt.ui.shared.ScoreCorrectionProviderDTO;
import com.sap.sailing.gwt.ui.shared.StrippedLeaderboardDTO;
import com.sap.sailing.gwt.ui.shared.SwissTimingArchiveConfigurationDTO;
import com.sap.sailing.gwt.ui.shared.SwissTimingConfigurationDTO;
import com.sap.sailing.gwt.ui.shared.SwissTimingEventRecordDTO;
import com.sap.sailing.gwt.ui.shared.SwissTimingRaceRecordDTO;
import com.sap.sailing.gwt.ui.shared.SwissTimingReplayRaceDTO;
import com.sap.sailing.gwt.ui.shared.TracTracConfigurationDTO;
import com.sap.sailing.gwt.ui.shared.TracTracRaceRecordDTO;
import com.sap.sailing.gwt.ui.shared.VenueDTO;
import com.sap.sailing.gwt.ui.shared.WindDTO;
import com.sap.sailing.gwt.ui.shared.WindInfoForRaceDTO;
/**
* The client side stub for the RPC service. Usually, when a <code>null</code> date is passed to
* the time-dependent service methods, an empty (non-<code>null</code>) result is returned.
*/
public interface SailingService extends RemoteService {
List<TracTracConfigurationDTO> getPreviousTracTracConfigurations() throws Exception;
List<RegattaDTO> getRegattas();
List<EventDTO> getEvents();
Pair<String, List<TracTracRaceRecordDTO>> listTracTracRacesInEvent(String eventJsonURL, boolean listHiddenRaces) throws Exception;
void trackWithTracTrac(RegattaIdentifier regattaToAddTo, Iterable<TracTracRaceRecordDTO> rrs, String liveURI,
String storedURI, String courseDesignUpdateURI, boolean trackWind, boolean correctWindByDeclination, boolean simulateWithStartTimeNow, String tracTracUsername, String tracTracPassword) throws Exception;
void trackWithSwissTiming(RegattaIdentifier regattaToAddTo, Iterable<SwissTimingRaceRecordDTO> rrs, String hostname, int port,
boolean trackWind, boolean correctWindByDeclination) throws Exception;
void replaySwissTimingRace(RegattaIdentifier regattaIdentifier, Iterable<SwissTimingReplayRaceDTO> replayRaces,
boolean trackWind, boolean correctWindByDeclination, boolean simulateWithStartTimeNow);
void storeTracTracConfiguration(String name, String jsonURL, String liveDataURI, String storedDataURI, String courseDesignUpdateURI, String tracTracUsername, String tracTracPassword) throws Exception;
void stopTrackingEvent(RegattaIdentifier eventIdentifier) throws Exception;
void stopTrackingRaces(Iterable<RegattaAndRaceIdentifier> racesToStopTracking) throws Exception;
void removeAndUntrackRaces(Iterable<RegattaAndRaceIdentifier> regattaNamesAndRaceNames);
WindInfoForRaceDTO getRawWindFixes(RegattaAndRaceIdentifier raceIdentifier, Collection<WindSource> windSources);
void setWind(RegattaAndRaceIdentifier raceIdentifier, WindDTO wind);
WindInfoForRaceDTO getAveragedWindInfo(RegattaAndRaceIdentifier raceIdentifier, Date from, long millisecondsStepWidth,
int numberOfFixes, Collection<String> windSourceTypeNames, boolean onlyUpToNewestEvent) throws NoWindException;
WindInfoForRaceDTO getAveragedWindInfo(RegattaAndRaceIdentifier raceIdentifier, Date from, Date to, long resolutionInMilliseconds,
Collection<String> windSourceTypeNames, boolean onlyUpToNewestEvent);
WindInfoForRaceDTO getAveragedWindInfo(RegattaAndRaceIdentifier raceIdentifier, Date from, long millisecondsStepWidth,
int numberOfFixes, double latDeg, double lngDeg, Collection<String> windSources) throws NoWindException;
CompactRaceMapDataDTO getRaceMapData(RegattaAndRaceIdentifier raceIdentifier, Date date, Map<String, Date> fromPerCompetitorIdAsString,
Map<String, Date> toPerCompetitorIdAsString, boolean extrapolate) throws NoWindException;
RaceTimesInfoDTO getRaceTimesInfo(RegattaAndRaceIdentifier raceIdentifier);
List<RaceTimesInfoDTO> getRaceTimesInfos(Collection<RegattaAndRaceIdentifier> raceIdentifiers);
CoursePositionsDTO getCoursePositions(RegattaAndRaceIdentifier raceIdentifier, Date date);
RaceCourseDTO getRaceCourse(RegattaAndRaceIdentifier raceIdentifier, Date date);
void removeWind(RegattaAndRaceIdentifier raceIdentifier, WindDTO windDTO);
public List<String> getLeaderboardNames();
IncrementalOrFullLeaderboardDTO getLeaderboardByName(String leaderboardName, Date date,
Collection<String> namesOfRaceColumnsForWhichToLoadLegDetails, boolean addOverallDetails,
String previousLeaderboardId) throws Exception;
List<StrippedLeaderboardDTO> getLeaderboards();
List<StrippedLeaderboardDTO> getLeaderboardsByRegatta(RegattaDTO regatta);
List<StrippedLeaderboardDTO> getLeaderboardsByEvent(EventDTO event);
StrippedLeaderboardDTO updateLeaderboard(String leaderboardName, String newLeaderboardName, String newLeaderboardDisplayName, int[] newDiscardingThreasholds, UUID newCourseAreaId);
StrippedLeaderboardDTO createFlexibleLeaderboard(String leaderboardName, String leaderboardDisplayName, int[] discardThresholds, ScoringSchemeType scoringSchemeType, UUID courseAreaId);
StrippedLeaderboardDTO createRegattaLeaderboard(RegattaIdentifier regattaIdentifier, String leaderboardDisplayName, int[] discardThresholds);
void removeLeaderboard(String leaderboardName);
void removeLeaderboards(Collection<String> leaderboardNames);
void renameLeaderboard(String leaderboardName, String newLeaderboardName);
void renameLeaderboardColumn(String leaderboardName, String oldColumnName, String newColumnName);
void removeLeaderboardColumn(String leaderboardName, String columnName);
void addColumnToLeaderboard(String columnName, String leaderboardName, boolean medalRace);
void moveLeaderboardColumnUp(String leaderboardName, String columnName);
void moveLeaderboardColumnDown(String leaderboardName, String columnName);
RegattaDTO createRegatta(String regattaName, String boatClassName,
RegattaCreationParametersDTO seriesNamesWithFleetNamesAndFleetOrderingAndMedal, boolean persistent,
ScoringSchemeType scoringSchemeType, UUID defaultCourseAreaId);
void removeRegatta(RegattaIdentifier regattaIdentifier);
void removeSeries(RegattaIdentifier regattaIdentifier, String seriesName);
void removeRegattas(Collection<RegattaIdentifier> regattas);
void updateRegatta(RegattaIdentifier regattaIdentifier, UUID defaultCourseAreaUuid, RegattaConfigurationDTO regattaConfiguration);
List<RaceColumnInSeriesDTO> addRaceColumnsToSeries(RegattaIdentifier regattaIdentifier, String seriesName, List<String> columnNames);
void updateSeries(RegattaIdentifier regattaIdentifier, String seriesName, String newSeriesName, boolean isMedal,
int[] resultDiscardingThresholds, boolean startsWithZeroScore,
boolean firstRaceIsNonDiscardableCarryForward, boolean hasSplitFleetScore, List<FleetDTO> fleets);
RaceColumnInSeriesDTO addRaceColumnToSeries(RegattaIdentifier regattaIdentifier, String seriesName, String columnName);
void removeRaceColumnsFromSeries(RegattaIdentifier regattaIdentifier, String seriesName, List<String> columnNames);
void removeRaceColumnFromSeries(RegattaIdentifier regattaIdentifier, String seriesName, String columnName);
void moveRaceColumnInSeriesUp(RegattaIdentifier regattaIdentifier, String seriesName, String columnName);
void moveRaceColumnInSeriesDown(RegattaIdentifier regattaIdentifier, String seriesName, String columnName);
boolean connectTrackedRaceToLeaderboardColumn(String leaderboardName, String raceColumnName,
String fleetName, RegattaAndRaceIdentifier raceIdentifier);
void disconnectLeaderboardColumnFromTrackedRace(String leaderboardName, String raceColumnName, String fleetName);
Map<String, RegattaAndRaceIdentifier> getRegattaAndRaceNameOfTrackedRaceConnectedToLeaderboardColumn(String leaderboardName, String raceColumnName);
void updateLeaderboardCarryValue(String leaderboardName, String competitorIdAsString, Double carriedPoints);
/**
* @return the new net points in {@link Pair#getA()} and the new total points in {@link Pair#getB()} for time point
* <code>date</code> after the max points reason has been updated to <code>maxPointsReasonAsString</code>.
*/
Triple<Double, Double, Boolean> updateLeaderboardMaxPointsReason(String leaderboardName, String competitorIdAsString,
String raceColumnName, MaxPointsReason maxPointsReason, Date date) throws NoWindException;
Triple<Double, Double, Boolean> updateLeaderboardScoreCorrection(String leaderboardName, String competitorIdAsString,
String columnName, Double correctedScore, Date date) throws NoWindException;
void updateCompetitorDisplayNameInLeaderboard(String leaderboardName, String competitorIdAsString, String displayName);
void updateIsMedalRace(String leaderboardName, String columnName, boolean isMedalRace);
List<SwissTimingConfigurationDTO> getPreviousSwissTimingConfigurations();
SwissTimingEventRecordDTO getRacesOfSwissTimingEvent(String eventJsonURL) throws Exception;
void storeSwissTimingConfiguration(String configName, String jsonURL, String hostname, int port);
String[] getCountryCodes();
Map<CompetitorDTO, List<GPSFixDTO>> getDouglasPoints(RegattaAndRaceIdentifier raceIdentifier,
Map<CompetitorDTO, Date> from, Map<CompetitorDTO, Date> to, double meters) throws NoWindException;
Map<CompetitorDTO, List<ManeuverDTO>> getManeuvers(RegattaAndRaceIdentifier raceIdentifier,
Map<CompetitorDTO, Date> from, Map<CompetitorDTO, Date> to) throws NoWindException;
List<StrippedLeaderboardDTO> getLeaderboardsByRaceAndRegatta(RaceDTO race, RegattaIdentifier regattaIdentifier);
List<LeaderboardGroupDTO> getLeaderboardGroups(boolean withGeoLocationData);
LeaderboardGroupDTO getLeaderboardGroupByName(String groupName, boolean withGeoLocationData);
void renameLeaderboardGroup(String oldName, String newName);
void removeLeaderboardGroups(Set<String> groupNames);
LeaderboardGroupDTO createLeaderboardGroup(String groupName, String description,
boolean displayGroupsInReverseOrder, int[] overallLeaderboardDiscardThresholds,
ScoringSchemeType overallLeaderboardScoringSchemeType);
void updateLeaderboardGroup(String oldName, String newName, String description, List<String> leaderboardNames,
int[] overallLeaderboardDiscardThresholds, ScoringSchemeType overallLeaderboardScoringSchemeType);
CompetitorsRaceDataDTO getCompetitorsRaceData(RegattaAndRaceIdentifier race, List<CompetitorDTO> competitors, Date from, Date to,
long stepSizeInMs, DetailType detailType, String leaderboardGroupName, String leaderboardName) throws NoWindException;
void setRaceIsKnownToStartUpwind(RegattaAndRaceIdentifier raceIdentifier, boolean raceIsKnownToStartUpwind);
void setWindSourcesToExclude(RegattaAndRaceIdentifier raceIdentifier, Iterable<WindSource> windSourcesToExclude);
ReplicationStateDTO getReplicaInfo();
void startReplicatingFromMaster(String messagingHost, String masterHost, String exchangeName, int servletPort, int messagingPort) throws Exception;
void updateRaceDelayToLive(RegattaAndRaceIdentifier regattaAndRaceIdentifier, long delayToLiveInMs);
void updateRacesDelayToLive(List<RegattaAndRaceIdentifier> regattaAndRaceIdentifiers, long delayToLiveInMs);
void updateEvent(UUID eventId, String eventName, Date startDate, Date endDate, VenueDTO venue, boolean isPublic,
List<String> regattaNames);
EventDTO createEvent(String eventName, Date startDate, Date endDate, String venueName, boolean isPublic, List<String> courseAreaNames);
void removeEvent(UUID eventId);
void removeEvents(Collection<UUID> eventIds);
void renameEvent(UUID eventId, String newName);
EventDTO getEventByName(String eventName);
EventDTO getEventById(UUID id);
Iterable<String> getScoreCorrectionProviderNames();
ScoreCorrectionProviderDTO getScoreCorrectionsOfProvider(String providerName) throws Exception;
RegattaScoreCorrectionDTO getScoreCorrections(String scoreCorrectionProviderName, String eventName, String boatClassName,
Date timePointWhenResultPublished) throws Exception;
void updateLeaderboardScoreCorrectionsAndMaxPointsReasons(BulkScoreCorrectionDTO updates) throws NoWindException;
WindInfoForRaceDTO getWindSourcesInfo(RegattaAndRaceIdentifier raceIdentifier);
List<String> getResultImportUrls(String resultProviderName);
void removeResultImportURLs(String resultProviderName, Set<String> toRemove) throws Exception;
void addResultImportUrl(String resultProviderName, String url) throws Exception;
Void updateLeaderboardScoreCorrectionMetadata(String leaderboardName, Date timePointOfLastCorrectionValidity,
String comment);
List<String> getUrlResultProviderNames();
void updateRaceCourse(RegattaAndRaceIdentifier raceIdentifier, List<Pair<ControlPointDTO, PassingInstruction>> controlPoints);
void addColumnsToLeaderboard(String leaderboardName, List<Pair<String, Boolean>> columnsToAdd);
void removeLeaderboardColumns(String leaderboardName, List<String> columnsToRemove);
StrippedLeaderboardDTO getLeaderboard(String leaderboardName);
void suppressCompetitorInLeaderboard(String leaderboardName, String competitorIdAsString, boolean suppressed);
void updateLeaderboardColumnFactor(String leaderboardName, String columnName, Double newFactor);
List<SwissTimingReplayRaceDTO> listSwissTiminigReplayRaces(String swissTimingUrl);
List<Triple<String, List<CompetitorDTO>, List<Double>>> getLeaderboardDataEntriesForAllRaceColumns(String leaderboardName,
Date date, DetailType detailType) throws Exception;
List<String> getOverallLeaderboardNamesContaining(String leaderboardName);
List<SwissTimingArchiveConfigurationDTO> getPreviousSwissTimingArchiveConfigurations();
void storeSwissTimingArchiveConfiguration(String swissTimingUrl);
PolarSheetGenerationResponse generatePolarSheetForRaces(List<RegattaAndRaceIdentifier> selectedRaces,
PolarSheetGenerationSettings settings, String name) throws Exception;
CourseAreaDTO createCourseArea(UUID eventId, String courseAreaName);
List<Pair<String, String>> getLeaderboardsNamesOfMetaleaderboard(String metaLeaderboardName);
Pair<String, LeaderboardType> checkLeaderboardName(String leaderboardName);
List<RaceGroupDTO> getRegattaStructureForEvent(UUID eventId);
List<RegattaOverviewEntryDTO> getRaceStateEntriesForRaceGroup(UUID eventId, List<UUID> visibleCourseAreas,
List<String> visibleRegattas, boolean showOnlyCurrentlyRunningRaces, boolean showOnlyRacesOfSameDay)
throws Exception;
List<RegattaOverviewEntryDTO> getRaceStateEntriesForLeaderboard(String leaderboardName,
boolean showOnlyCurrentlyRunningRaces, boolean showOnlyRacesOfSameDay, List<String> visibleRegattas)
throws Exception;
String getBuildVersion();
void stopReplicatingFromMaster();
void stopAllReplicas();
void stopSingleReplicaInstance(String identifier);
void reloadRaceLog(String leaderboardName, RaceColumnDTO raceColumnDTO, FleetDTO fleet);
RaceLogDTO getRaceLog(String leaderboardName, RaceColumnDTO raceColumnDTO, FleetDTO fleet);
List<String> getLeaderboardGroupNamesFromRemoteServer(String host);
UUID importMasterData(String host, String[] groupNames, boolean override, boolean compress, boolean exportWind);
DataImportProgress getImportOperationProgress(UUID id);
Iterable<CompetitorDTO> getCompetitors();
package com.sap.sailing.gwt.ui.client;
import java.io.Serializable;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import com.google.gwt.user.client.rpc.RemoteService;
import com.sap.sailing.domain.base.RaceDefinition;
import com.sap.sailing.domain.common.DataImportProgress;
import com.sap.sailing.domain.common.DetailType;
import com.sap.sailing.domain.common.LeaderboardType;
import com.sap.sailing.domain.common.MaxPointsReason;
import com.sap.sailing.domain.common.NoWindException;
import com.sap.sailing.domain.common.PassingInstruction;
import com.sap.sailing.domain.common.PolarSheetGenerationResponse;
import com.sap.sailing.domain.common.PolarSheetGenerationSettings;
import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
import com.sap.sailing.domain.common.RegattaIdentifier;
import com.sap.sailing.domain.common.ScoringSchemeType;
import com.sap.sailing.domain.common.WindSource;
import com.sap.sailing.domain.common.configuration.DeviceConfigurationMatcherType;
import com.sap.sailing.domain.common.dto.CompetitorDTO;
import com.sap.sailing.domain.common.dto.FleetDTO;
import com.sap.sailing.domain.common.dto.IncrementalOrFullLeaderboardDTO;
import com.sap.sailing.domain.common.dto.PositionDTO;
import com.sap.sailing.domain.common.dto.RaceColumnDTO;
import com.sap.sailing.domain.common.dto.RaceColumnInSeriesDTO;
import com.sap.sailing.domain.common.dto.RaceDTO;
import com.sap.sailing.domain.common.dto.RegattaCreationParametersDTO;
import com.sap.sailing.domain.common.impl.Util.Pair;
import com.sap.sailing.domain.common.impl.Util.Triple;
import com.sap.sailing.domain.common.racelog.RacingProcedureType;
import com.sap.sailing.domain.common.racelog.tracking.NoCorrespondingServiceRegisteredException;
import com.sap.sailing.domain.common.racelog.tracking.NotDenotedForRaceLogTrackingException;
import com.sap.sailing.domain.common.racelog.tracking.NotRevokableException;
import com.sap.sailing.domain.common.racelog.tracking.TransformationException;
import com.sap.sailing.domain.racelog.RaceLog;
import com.sap.sailing.domain.racelog.Revokable;
import com.sap.sailing.domain.racelog.tracking.DenoteForTrackingEvent;
import com.sap.sailing.domain.racelog.tracking.GPSFixStore;
import com.sap.sailing.domain.racelogtracking.RaceLogTrackingAdapter;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.gwt.ui.shared.BulkScoreCorrectionDTO;
import com.sap.sailing.gwt.ui.shared.CompactRaceMapDataDTO;
import com.sap.sailing.gwt.ui.shared.CompetitorsRaceDataDTO;
import com.sap.sailing.gwt.ui.shared.ControlPointDTO;
import com.sap.sailing.gwt.ui.shared.CourseAreaDTO;
import com.sap.sailing.gwt.ui.shared.CoursePositionsDTO;
import com.sap.sailing.gwt.ui.shared.DeviceConfigurationDTO;
import com.sap.sailing.gwt.ui.shared.DeviceConfigurationDTO.RegattaConfigurationDTO;
import com.sap.sailing.gwt.ui.shared.DeviceConfigurationMatcherDTO;
import com.sap.sailing.gwt.ui.shared.DeviceMappingDTO;
import com.sap.sailing.gwt.ui.shared.EventDTO;
import com.sap.sailing.gwt.ui.shared.GPSFixDTO;
import com.sap.sailing.gwt.ui.shared.LeaderboardGroupDTO;
import com.sap.sailing.gwt.ui.shared.ManeuverDTO;
import com.sap.sailing.gwt.ui.shared.MarkDTO;
import com.sap.sailing.gwt.ui.shared.RaceCourseDTO;
import com.sap.sailing.gwt.ui.shared.RaceGroupDTO;
import com.sap.sailing.gwt.ui.shared.RaceLogDTO;
import com.sap.sailing.gwt.ui.shared.RaceLogSetStartTimeAndProcedureDTO;
import com.sap.sailing.gwt.ui.shared.RaceTimesInfoDTO;
import com.sap.sailing.gwt.ui.shared.RegattaDTO;
import com.sap.sailing.gwt.ui.shared.RegattaOverviewEntryDTO;
import com.sap.sailing.gwt.ui.shared.RegattaScoreCorrectionDTO;
import com.sap.sailing.gwt.ui.shared.RemoteSailingServerReferenceDTO;
import com.sap.sailing.gwt.ui.shared.ReplicationStateDTO;
import com.sap.sailing.gwt.ui.shared.ScoreCorrectionProviderDTO;
import com.sap.sailing.gwt.ui.shared.StrippedLeaderboardDTO;
import com.sap.sailing.gwt.ui.shared.SwissTimingArchiveConfigurationDTO;
import com.sap.sailing.gwt.ui.shared.SwissTimingConfigurationDTO;
import com.sap.sailing.gwt.ui.shared.SwissTimingEventRecordDTO;
import com.sap.sailing.gwt.ui.shared.SwissTimingRaceRecordDTO;
import com.sap.sailing.gwt.ui.shared.SwissTimingReplayRaceDTO;
import com.sap.sailing.gwt.ui.shared.TracTracConfigurationDTO;
import com.sap.sailing.gwt.ui.shared.TracTracRaceRecordDTO;
import com.sap.sailing.gwt.ui.shared.VenueDTO;
import com.sap.sailing.gwt.ui.shared.WindDTO;
import com.sap.sailing.gwt.ui.shared.WindInfoForRaceDTO;
/**
* The client side stub for the RPC service. Usually, when a <code>null</code> date is passed to
* the time-dependent service methods, an empty (non-<code>null</code>) result is returned.
*/
public interface SailingService extends RemoteService {
List<TracTracConfigurationDTO> getPreviousTracTracConfigurations() throws Exception;
List<RegattaDTO> getRegattas();
List<EventDTO> getEvents();
List<RemoteSailingServerReferenceDTO> getPublicEventsOfAllSailingServers();
Pair<String, List<TracTracRaceRecordDTO>> listTracTracRacesInEvent(String eventJsonURL, boolean listHiddenRaces) throws Exception;
void trackWithTracTrac(RegattaIdentifier regattaToAddTo, Iterable<TracTracRaceRecordDTO> rrs, String liveURI,
String storedURI, String courseDesignUpdateURI, boolean trackWind, boolean correctWindByDeclination, boolean simulateWithStartTimeNow, String tracTracUsername, String tracTracPassword) throws Exception;
void trackWithSwissTiming(RegattaIdentifier regattaToAddTo, Iterable<SwissTimingRaceRecordDTO> rrs, String hostname, int port,
boolean trackWind, boolean correctWindByDeclination) throws Exception;
void replaySwissTimingRace(RegattaIdentifier regattaIdentifier, Iterable<SwissTimingReplayRaceDTO> replayRaces,
boolean trackWind, boolean correctWindByDeclination, boolean simulateWithStartTimeNow);
void storeTracTracConfiguration(String name, String jsonURL, String liveDataURI, String storedDataURI, String courseDesignUpdateURI, String tracTracUsername, String tracTracPassword) throws Exception;
void stopTrackingEvent(RegattaIdentifier eventIdentifier) throws Exception;
void stopTrackingRaces(Iterable<RegattaAndRaceIdentifier> racesToStopTracking) throws Exception;
void removeAndUntrackRaces(Iterable<RegattaAndRaceIdentifier> regattaNamesAndRaceNames);
WindInfoForRaceDTO getRawWindFixes(RegattaAndRaceIdentifier raceIdentifier, Collection<WindSource> windSources);
void setWind(RegattaAndRaceIdentifier raceIdentifier, WindDTO wind);
WindInfoForRaceDTO getAveragedWindInfo(RegattaAndRaceIdentifier raceIdentifier, Date from, long millisecondsStepWidth,
int numberOfFixes, Collection<String> windSourceTypeNames, boolean onlyUpToNewestEvent) throws NoWindException;
WindInfoForRaceDTO getAveragedWindInfo(RegattaAndRaceIdentifier raceIdentifier, Date from, Date to, long resolutionInMilliseconds,
Collection<String> windSourceTypeNames, boolean onlyUpToNewestEvent);
WindInfoForRaceDTO getAveragedWindInfo(RegattaAndRaceIdentifier raceIdentifier, Date from, long millisecondsStepWidth,
int numberOfFixes, double latDeg, double lngDeg, Collection<String> windSources) throws NoWindException;
CompactRaceMapDataDTO getRaceMapData(RegattaAndRaceIdentifier raceIdentifier, Date date, Map<String, Date> fromPerCompetitorIdAsString,
Map<String, Date> toPerCompetitorIdAsString, boolean extrapolate) throws NoWindException;
RaceTimesInfoDTO getRaceTimesInfo(RegattaAndRaceIdentifier raceIdentifier);
List<RaceTimesInfoDTO> getRaceTimesInfos(Collection<RegattaAndRaceIdentifier> raceIdentifiers);
CoursePositionsDTO getCoursePositions(RegattaAndRaceIdentifier raceIdentifier, Date date);
RaceCourseDTO getRaceCourse(RegattaAndRaceIdentifier raceIdentifier, Date date);
void removeWind(RegattaAndRaceIdentifier raceIdentifier, WindDTO windDTO);
public List<String> getLeaderboardNames();
IncrementalOrFullLeaderboardDTO getLeaderboardByName(String leaderboardName, Date date,
Collection<String> namesOfRaceColumnsForWhichToLoadLegDetails, boolean addOverallDetails,
String previousLeaderboardId) throws Exception;
List<StrippedLeaderboardDTO> getLeaderboards();
List<StrippedLeaderboardDTO> getLeaderboardsByRegatta(RegattaDTO regatta);
List<StrippedLeaderboardDTO> getLeaderboardsByEvent(EventDTO event);
StrippedLeaderboardDTO updateLeaderboard(String leaderboardName, String newLeaderboardName, String newLeaderboardDisplayName, int[] newDiscardingThreasholds, UUID newCourseAreaId);
StrippedLeaderboardDTO createFlexibleLeaderboard(String leaderboardName, String leaderboardDisplayName, int[] discardThresholds, ScoringSchemeType scoringSchemeType, UUID courseAreaId);
StrippedLeaderboardDTO createRegattaLeaderboard(RegattaIdentifier regattaIdentifier, String leaderboardDisplayName, int[] discardThresholds);
void removeLeaderboard(String leaderboardName);
void removeLeaderboards(Collection<String> leaderboardNames);
void renameLeaderboard(String leaderboardName, String newLeaderboardName);
void renameLeaderboardColumn(String leaderboardName, String oldColumnName, String newColumnName);
void removeLeaderboardColumn(String leaderboardName, String columnName);
void addColumnToLeaderboard(String columnName, String leaderboardName, boolean medalRace);
void moveLeaderboardColumnUp(String leaderboardName, String columnName);
void moveLeaderboardColumnDown(String leaderboardName, String columnName);
RegattaDTO createRegatta(String regattaName, String boatClassName,
RegattaCreationParametersDTO seriesNamesWithFleetNamesAndFleetOrderingAndMedal, boolean persistent,
ScoringSchemeType scoringSchemeType, UUID defaultCourseAreaId);
void removeRegatta(RegattaIdentifier regattaIdentifier);
void removeSeries(RegattaIdentifier regattaIdentifier, String seriesName);
void removeRegattas(Collection<RegattaIdentifier> regattas);
void updateRegatta(RegattaIdentifier regattaIdentifier, UUID defaultCourseAreaUuid, RegattaConfigurationDTO regattaConfiguration);
List<RaceColumnInSeriesDTO> addRaceColumnsToSeries(RegattaIdentifier regattaIdentifier, String seriesName, List<String> columnNames);
void updateSeries(RegattaIdentifier regattaIdentifier, String seriesName, String newSeriesName, boolean isMedal,
int[] resultDiscardingThresholds, boolean startsWithZeroScore,
boolean firstRaceIsNonDiscardableCarryForward, boolean hasSplitFleetScore, List<FleetDTO> fleets);
RaceColumnInSeriesDTO addRaceColumnToSeries(RegattaIdentifier regattaIdentifier, String seriesName, String columnName);
void removeRaceColumnsFromSeries(RegattaIdentifier regattaIdentifier, String seriesName, List<String> columnNames);
void removeRaceColumnFromSeries(RegattaIdentifier regattaIdentifier, String seriesName, String columnName);
void moveRaceColumnInSeriesUp(RegattaIdentifier regattaIdentifier, String seriesName, String columnName);
void moveRaceColumnInSeriesDown(RegattaIdentifier regattaIdentifier, String seriesName, String columnName);
boolean connectTrackedRaceToLeaderboardColumn(String leaderboardName, String raceColumnName,
String fleetName, RegattaAndRaceIdentifier raceIdentifier);
void disconnectLeaderboardColumnFromTrackedRace(String leaderboardName, String raceColumnName, String fleetName);
Map<String, RegattaAndRaceIdentifier> getRegattaAndRaceNameOfTrackedRaceConnectedToLeaderboardColumn(String leaderboardName, String raceColumnName);
void updateLeaderboardCarryValue(String leaderboardName, String competitorIdAsString, Double carriedPoints);
/**
* @return the new net points in {@link Pair#getA()} and the new total points in {@link Pair#getB()} for time point
* <code>date</code> after the max points reason has been updated to <code>maxPointsReasonAsString</code>.
*/
Triple<Double, Double, Boolean> updateLeaderboardMaxPointsReason(String leaderboardName, String competitorIdAsString,
String raceColumnName, MaxPointsReason maxPointsReason, Date date) throws NoWindException;
Triple<Double, Double, Boolean> updateLeaderboardScoreCorrection(String leaderboardName, String competitorIdAsString,
String columnName, Double correctedScore, Date date) throws NoWindException;
void updateCompetitorDisplayNameInLeaderboard(String leaderboardName, String competitorIdAsString, String displayName);
void updateIsMedalRace(String leaderboardName, String columnName, boolean isMedalRace);
List<SwissTimingConfigurationDTO> getPreviousSwissTimingConfigurations();
SwissTimingEventRecordDTO getRacesOfSwissTimingEvent(String eventJsonURL) throws Exception;
void storeSwissTimingConfiguration(String configName, String jsonURL, String hostname, int port);
String[] getCountryCodes();
Map<CompetitorDTO, List<GPSFixDTO>> getDouglasPoints(RegattaAndRaceIdentifier raceIdentifier,
Map<CompetitorDTO, Date> from, Map<CompetitorDTO, Date> to, double meters) throws NoWindException;
Map<CompetitorDTO, List<ManeuverDTO>> getManeuvers(RegattaAndRaceIdentifier raceIdentifier,
Map<CompetitorDTO, Date> from, Map<CompetitorDTO, Date> to) throws NoWindException;
List<StrippedLeaderboardDTO> getLeaderboardsByRaceAndRegatta(RaceDTO race, RegattaIdentifier regattaIdentifier);
List<LeaderboardGroupDTO> getLeaderboardGroups(boolean withGeoLocationData);
LeaderboardGroupDTO getLeaderboardGroupByName(String groupName, boolean withGeoLocationData);
void renameLeaderboardGroup(String oldName, String newName);
void removeLeaderboardGroups(Set<String> groupNames);
LeaderboardGroupDTO createLeaderboardGroup(String groupName, String description,
boolean displayGroupsInReverseOrder, int[] overallLeaderboardDiscardThresholds,
ScoringSchemeType overallLeaderboardScoringSchemeType);
void updateLeaderboardGroup(String oldName, String newName, String description, List<String> leaderboardNames,
int[] overallLeaderboardDiscardThresholds, ScoringSchemeType overallLeaderboardScoringSchemeType);
CompetitorsRaceDataDTO getCompetitorsRaceData(RegattaAndRaceIdentifier race, List<CompetitorDTO> competitors, Date from, Date to,
long stepSizeInMs, DetailType detailType, String leaderboardGroupName, String leaderboardName) throws NoWindException;
void setRaceIsKnownToStartUpwind(RegattaAndRaceIdentifier raceIdentifier, boolean raceIsKnownToStartUpwind);
void setWindSourcesToExclude(RegattaAndRaceIdentifier raceIdentifier, Iterable<WindSource> windSourcesToExclude);
ReplicationStateDTO getReplicaInfo();
void startReplicatingFromMaster(String messagingHost, String masterHost, String exchangeName, int servletPort, int messagingPort) throws Exception;
void updateRaceDelayToLive(RegattaAndRaceIdentifier regattaAndRaceIdentifier, long delayToLiveInMs);
void updateRacesDelayToLive(List<RegattaAndRaceIdentifier> regattaAndRaceIdentifiers, long delayToLiveInMs);
void updateEvent(UUID eventId, String eventName, Date startDate, Date endDate, VenueDTO venue, boolean isPublic,
List<String> regattaNames);
EventDTO createEvent(String eventName, Date startDate, Date endDate, String venueName, boolean isPublic, List<String> courseAreaNames);
void removeEvent(UUID eventId);
void removeEvents(Collection<UUID> eventIds);
void renameEvent(UUID eventId, String newName);
EventDTO getEventByName(String eventName);
EventDTO getEventById(UUID id);
Iterable<String> getScoreCorrectionProviderNames();
ScoreCorrectionProviderDTO getScoreCorrectionsOfProvider(String providerName) throws Exception;
RegattaScoreCorrectionDTO getScoreCorrections(String scoreCorrectionProviderName, String eventName, String boatClassName,
Date timePointWhenResultPublished) throws Exception;
void updateLeaderboardScoreCorrectionsAndMaxPointsReasons(BulkScoreCorrectionDTO updates) throws NoWindException;
WindInfoForRaceDTO getWindSourcesInfo(RegattaAndRaceIdentifier raceIdentifier);
List<RemoteSailingServerReferenceDTO> getRemoteSailingServerReferences();
void removeSailingServers(Set<String> toRemove) throws Exception;
RemoteSailingServerReferenceDTO addRemoteSailingServerReference(RemoteSailingServerReferenceDTO sailingServer) throws Exception;
List<String> getResultImportUrls(String resultProviderName);
void removeResultImportURLs(String resultProviderName, Set<String> toRemove) throws Exception;
void addResultImportUrl(String resultProviderName, String url) throws Exception;
void updateLeaderboardScoreCorrectionMetadata(String leaderboardName, Date timePointOfLastCorrectionValidity,
String comment);
List<String> getUrlResultProviderNames();
void updateRaceCourse(RegattaAndRaceIdentifier raceIdentifier, List<Pair<ControlPointDTO, PassingInstruction>> controlPoints);
void addColumnsToLeaderboard(String leaderboardName, List<Pair<String, Boolean>> columnsToAdd);
void removeLeaderboardColumns(String leaderboardName, List<String> columnsToRemove);
StrippedLeaderboardDTO getLeaderboard(String leaderboardName);
void suppressCompetitorInLeaderboard(String leaderboardName, String competitorIdAsString, boolean suppressed);
void updateLeaderboardColumnFactor(String leaderboardName, String columnName, Double newFactor);
List<SwissTimingReplayRaceDTO> listSwissTiminigReplayRaces(String swissTimingUrl);
List<Triple<String, List<CompetitorDTO>, List<Double>>> getLeaderboardDataEntriesForAllRaceColumns(String leaderboardName,
Date date, DetailType detailType) throws Exception;
List<String> getOverallLeaderboardNamesContaining(String leaderboardName);
List<SwissTimingArchiveConfigurationDTO> getPreviousSwissTimingArchiveConfigurations();
void storeSwissTimingArchiveConfiguration(String swissTimingUrl);
PolarSheetGenerationResponse generatePolarSheetForRaces(List<RegattaAndRaceIdentifier> selectedRaces,
PolarSheetGenerationSettings settings, String name) throws Exception;
CourseAreaDTO createCourseArea(UUID eventId, String courseAreaName);
List<Pair<String, String>> getLeaderboardsNamesOfMetaleaderboard(String metaLeaderboardName);
Pair<String, LeaderboardType> checkLeaderboardName(String leaderboardName);
List<RaceGroupDTO> getRegattaStructureForEvent(UUID eventId);
List<RegattaOverviewEntryDTO> getRaceStateEntriesForRaceGroup(UUID eventId, List<UUID> visibleCourseAreas,
List<String> visibleRegattas, boolean showOnlyCurrentlyRunningRaces, boolean showOnlyRacesOfSameDay)
throws Exception;
List<RegattaOverviewEntryDTO> getRaceStateEntriesForLeaderboard(String leaderboardName,
boolean showOnlyCurrentlyRunningRaces, boolean showOnlyRacesOfSameDay, List<String> visibleRegattas)
throws Exception;
String getBuildVersion();
void stopReplicatingFromMaster();
void stopAllReplicas();
void stopSingleReplicaInstance(String identifier);
void reloadRaceLog(String leaderboardName, RaceColumnDTO raceColumnDTO, FleetDTO fleet);
RaceLogDTO getRaceLog(String leaderboardName, RaceColumnDTO raceColumnDTO, FleetDTO fleet);
List<String> getLeaderboardGroupNamesFromRemoteServer(String host);
UUID importMasterData(String host, String[] groupNames, boolean override, boolean compress, boolean exportWind);
DataImportProgress getImportOperationProgress(UUID id);
Iterable<CompetitorDTO> getCompetitors();
/**
*
* @param leaderboardName
@@ -375,32 +384,32 @@ public interface SailingService extends RemoteService {
* @return
*/
Iterable<CompetitorDTO> getCompetitorsOfLeaderboard(String leaderboardName, boolean lookInRaceLogs);
CompetitorDTO addOrUpdateCompetitor(CompetitorDTO competitor);
void allowCompetitorResetToDefaults(Iterable<CompetitorDTO> competitors);
List<DeviceConfigurationMatcherDTO> getDeviceConfigurationMatchers();
DeviceConfigurationDTO getDeviceConfiguration(DeviceConfigurationMatcherDTO matcher);
DeviceConfigurationMatcherDTO createOrUpdateDeviceConfiguration(DeviceConfigurationMatcherDTO matcherDTO, DeviceConfigurationDTO configurationDTO);
boolean removeDeviceConfiguration(DeviceConfigurationMatcherType type, List<String> clientIds);
boolean setStartTimeAndProcedure(RaceLogSetStartTimeAndProcedureDTO dto);
Triple<Date, Integer, RacingProcedureType> getStartTimeAndProcedure(String leaderboardName, String raceColumnName, String fleetName);
Iterable<String> getAllIgtimiAccountEmailAddresses();
String getIgtimiAuthorizationUrl();
boolean authorizeAccessToIgtimiUser(String eMailAddress, String password) throws Exception;
void removeIgtimiAccount(String eMailOfAccountToRemove);
Map<RegattaAndRaceIdentifier, Integer> importWindFromIgtimi(List<RaceDTO> selectedRaces) throws Exception;
void allowCompetitorResetToDefaults(Iterable<CompetitorDTO> competitors);
List<DeviceConfigurationMatcherDTO> getDeviceConfigurationMatchers();
DeviceConfigurationDTO getDeviceConfiguration(DeviceConfigurationMatcherDTO matcher);
DeviceConfigurationMatcherDTO createOrUpdateDeviceConfiguration(DeviceConfigurationMatcherDTO matcherDTO, DeviceConfigurationDTO configurationDTO);
boolean removeDeviceConfiguration(DeviceConfigurationMatcherType type, List<String> clientIds);
boolean setStartTimeAndProcedure(RaceLogSetStartTimeAndProcedureDTO dto);
Triple<Date, Integer, RacingProcedureType> getStartTimeAndProcedure(String leaderboardName, String raceColumnName, String fleetName);
Iterable<String> getAllIgtimiAccountEmailAddresses();
String getIgtimiAuthorizationUrl();
boolean authorizeAccessToIgtimiUser(String eMailAddress, String password) throws Exception;
void removeIgtimiAccount(String eMailOfAccountToRemove);
Map<RegattaAndRaceIdentifier, Integer> importWindFromIgtimi(List<RaceDTO> selectedRaces) throws Exception;
void denoteForRaceLogTracking(String leaderboardName, String raceColumnName, String fleetName) throws Exception;
@@ -464,4 +473,4 @@ public interface SailingService extends RemoteService {
*/
void revokeRaceLogEvents(String leaderboardName, String raceColumnName, String fleetName, List<Serializable> eventIds)
throws NotRevokableException;
}
}
@@ -358,6 +358,12 @@ public interface StringMessages extends Messages {
String pleaseEnterNonEmptyUrl();
String addResultImportUrl();
String errorAddingResultImportUrl(String message);
String sailingServers();
String errorRefreshingSailingServers(String message);
String successfullyUpdatedSailingServers();
String errorRemovingSailingServers(String message);
String addSailingServer();
String errorAddingSailingServer(String message);
String errorTryingToObtainLeaderboardContents(String message);
String lastScoreUpdate();
String warningBrowserUnsupported();
@@ -1020,4 +1026,6 @@ public interface StringMessages extends Messages {
String selectAtLeastOne();
String selectRaces();
String importFixes();
String noSailingServerInstancesYet();
String registeredSailingServerInstances();
}
@@ -385,6 +385,12 @@ errorRemovingResultImportUrls=Error removing result import URLs: {0}
pleaseEnterNonEmptyUrl=Please enter a URL.
addResultImportUrl=Add result import URL
errorAddingResultImportUrl=Error adding result import URL: {0}
sailingServers=SailingServer instances
errorRefreshingSailingServers=Error refreshing SailingServer instances: {0}
successfullyUpdatedSailingServers=Successfully updated SailingServer instances
errorRemovingSailingServers=Error removing SailingServer instances: {0}
addSailingServer=Add SailingServer instance
errorAddingSailingServer=Error adding SailingServer instance: {0}
errorTryingToObtainLeaderboardContents=Error trying to obtain leaderboard contents: {0}
lastScoreUpdate=Last Score Update
warningBrowserUnsupported=This website is not optimized for your browser. Please switch to a newer one like Firefox or Chrome.
@@ -1006,3 +1012,5 @@ competitorPool=Competitor Pool
selectAtLeastOne=Select at least one
selectRaces=Select the Races
importFixes=Import Fixes
noSailingServerInstancesYet=No Sailing Server instances yet.
registeredSailingServerInstances=Registered Sailing Server Instances
@@ -386,6 +386,12 @@ errorRemovingResultImportUrls=Fehler beim Löschen von Ergebnisimport URLs: {0}
pleaseEnterNonEmptyUrl=Bitte gebe eine URL ein.
addResultImportUrl=Ergebnisimport-URL hinzufügen
errorAddingResultImportUrl=Fehler beim Hinzufügen einer Ergebnisimport URL: {0}
sailingServers=SailingServer Instanzen
errorRefreshingSailingServers=Fehler beim Aktualisieren der SailingServer Instanzen: {0}
successfullyUpdatedSailingServers=SailingServer Instanzen erfolgreich aktualisiert
errorRemovingSailingServers=Fehler beim Löschen von SailingServer Instanzen: {0}
addSailingServer=SailingServer Instanz hinzufügen
errorAddingSailingServer=Fehler beim Hinzufügen einer SailingServer Instanz: {0}
errorTryingToObtainLeaderboardContents=Fehler beim Berechnen der Ergebnisliste: {0}
lastScoreUpdate=Letzte Ergebnisänderung
warningBrowserUnsupported=Diese Webseite ist für Ihren Browser nicht optimiert. Wir empfehlen den Firefox oder Chrome in der neuesten Version.
@@ -1000,3 +1006,5 @@ twoMarkControlPoint=2-Marken-Kontrollpunkt
registeredCompetitors=Registrierte Teams
competitorPool=Team Vorrat
importFixes=Fixes importieren
noSailingServerInstancesYet=Bisher noch keine Sailing Server-Instanzen.
registeredSailingServerInstances=Registrierte Sailing Server-Instanzen
@@ -968,3 +968,5 @@ competitorPool=Competitor Pool
selectAtLeastOne=Select at least one
selectRaces=Select the Races
importFixes=Import Fixes
noSailingServerInstancesYet=No Sailing Server instances yet.
registeredSailingServerInstances=Registered Sailing Server Instances
@@ -163,7 +163,7 @@ public class WindChart extends AbstractRaceChart implements Component<WindChartS
}));
timePlotLine = chart.getXAxis().createPlotLine().setColor("#656565").setWidth(1.5).setDashStyle(DashStyle.SOLID);
chart.getYAxis(0).setAxisTitleText(stringMessages.fromDeg()).setStartOnTick(false).setShowFirstLabel(false)
chart.getYAxis(0).setAxisTitleText(stringMessages.fromDeg()).setStartOnTick(false)
.setLabels(new YAxisLabels().setFormatter(new AxisLabelsFormatter() {
@Override
public String format(AxisLabelsData axisLabelsData) {
@@ -171,10 +171,8 @@ public class WindChart extends AbstractRaceChart implements Component<WindChartS
return new Long(value < 0 ? value + 360 : value).toString();
}
}));
chart.getYAxis(1).setOpposite(true).setAxisTitleText(stringMessages.speed()+" ("+stringMessages.knotsUnit()+")")
.setStartOnTick(false).setShowFirstLabel(false).setGridLineWidth(0).setMinorGridLineWidth(0);
chart.getYAxis(1).setOpposite(true).setAxisTitleText(stringMessages.speed()+" ("+stringMessages.knotsUnit()+")").setMin(0)
.setMaxPadding(0.05).setStartOnTick(false).setGridLineWidth(0).setMinorGridLineWidth(0);
if (compactChart) {
chart.setSpacingBottom(10).setSpacingLeft(10).setSpacingRight(10).setSpacingTop(2)
.setOption("legend/margin", 2)
@@ -200,7 +198,7 @@ public class WindChart extends AbstractRaceChart implements Component<WindChartS
List<Series> currentSeries = Arrays.asList(chart.getSeries());
Set<Series> visibleSeries = new HashSet<Series>(currentSeries);
if(settings.isShowWindDirectionsSeries()) {
if (settings.isShowWindDirectionsSeries()) {
for (Map.Entry<WindSource, Series> e : windSourceDirectionSeries.entrySet()) {
Series series = e.getValue();
if (settings.getWindDirectionSourcesToDisplay().contains(e.getKey().getType())) {
@@ -245,7 +243,6 @@ public class WindChart extends AbstractRaceChart implements Component<WindChartS
}
}
}
chart.redraw();
}
@@ -338,7 +335,7 @@ public class WindChart extends AbstractRaceChart implements Component<WindChartS
newMaxTimepoint = wind.requestTimepoint;
}
if((timeOfEarliestRequestInMillis == null || wind.requestTimepoint < timeOfEarliestRequestInMillis) ||
if ((timeOfEarliestRequestInMillis == null || wind.requestTimepoint < timeOfEarliestRequestInMillis) ||
timeOfLatestRequestInMillis == null || wind.requestTimepoint > timeOfLatestRequestInMillis) {
Point newDirectionPoint = new Point(wind.requestTimepoint, wind.dampenedTrueWindFromDeg);
if (wind.dampenedTrueWindSpeedInKnots != null) {
@@ -11,6 +11,7 @@ import com.google.gwt.user.cellview.client.ColumnSortEvent;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.view.client.ListDataProvider;
import com.sap.sailing.domain.common.impl.Util;
import com.sap.sailing.gwt.ui.client.shared.filter.AbstractListFilter;
/**
@@ -21,7 +22,11 @@ import com.sap.sailing.gwt.ui.client.shared.filter.AbstractListFilter;
* known to an instance of this class). To be initiated the method {@link #getSearchableStrings(Object)} has to be
* defined, which gets those Strings from a <code>T</code> that should be considered when filtering, e.g. name or
* boatClass. The cell table can be sorted independently from the text box (e.g. after adding new objects) by calling
* the method {@link #updateAll(Iterable)} which then runs the filter over the new selection.
* the method {@link #updateAll(Iterable)} which then runs the filter over the new selection.<p>
*
* Note that this panel does <em>not</em> contain the table that it filters. With this, this class's clients are free
* to position the table wherever they want, not necessarily related to the text box provided by this panel in any
* specific way.
*
* @param <T>
* @author Nicolas Klose, Axel Uhl
@@ -92,13 +97,18 @@ public abstract class AbstractFilterablePanel<T> extends HorizontalPanel {
/**
* Adds an object and applies the search filter.
*/
public void add(T object){
public void add(T object) {
all.add(object);
filter();
}
public void filter(){
filtered.getList().clear();
public void addAll(Iterable<T> objects) {
Util.addAll(objects, all);
filter();
}
public void filter() {
filtered.getList().clear();
filtered.getList().addAll(filterer.applyFilter(getTextBox().getText(), all));
sort();
}
@@ -111,7 +121,7 @@ public abstract class AbstractFilterablePanel<T> extends HorizontalPanel {
return textBox;
}
public Collection<T> getAll() {
public Iterable<T> getAll() {
return all;
}
}
@@ -13,7 +13,11 @@ import com.sap.sailing.gwt.ui.client.shared.filter.AbstractListFilter;
* known to an instance of this class). To be initiated the method {@link #getSearchableStrings(Object)} has to be
* defined, which gets those Strings from a <code>T</code> that should be considered when filtering, e.g. name or
* boatClass. The cell table can be sorted independently from the text box (e.g. after adding new objects) by calling
* the method {@link #updateAll(Iterable)} which then runs the filter over the new selection.
* the method {@link #updateAll(Iterable)} which then runs the filter over the new selection.</p>
*
* Note that this panel does <em>not</em> contain the table that it filters. With this, this class's clients are free
* to position the table wherever they want, not necessarily related to the text box provided by this panel in any
* specific way.
*
* @param <T>
* @author Nicolas Klose, Axel Uhl
@@ -148,6 +148,7 @@ public abstract class ExpandableSortableColumn<C> extends SortableColumn<Leaderb
*/
public void toggleExpansion() {
if (isExpansionEnabled()) {
final boolean oldBusyState = getLeaderboardPanel().getBusyIndicator().isBusy();
getLeaderboardPanel().getBusyIndicator().setBusy(true);
setTogglingInProcess(true);
final CellTable<LeaderboardRowDTO> table = getLeaderboardPanel().getLeaderboardTable();
@@ -175,7 +176,7 @@ public abstract class ExpandableSortableColumn<C> extends SortableColumn<Leaderb
}
getLeaderboardPanel().getLeaderboardTable().redraw();
}
getLeaderboardPanel().getBusyIndicator().setBusy(false);
getLeaderboardPanel().getBusyIndicator().setBusy(oldBusyState);
setTogglingInProcess(false);
}
});
@@ -335,12 +335,17 @@ public class LeaderboardPanel extends SimplePanel implements TimeListener, PlayS
return resources.settingsIcon();
}
public void updateSettings(LeaderboardSettings newSettings) {
public void updateSettings(final LeaderboardSettings newSettings) {
boolean oldShallAddOverallDetails = shallAddOverallDetails();
if (newSettings.getOverallDetailsToShow() != null) {
selectedOverallDetailColumns.clear();
selectedOverallDetailColumns.addAll(newSettings.getOverallDetailsToShow());
}
if (!newSettings.isUpdateUponPlayStateChange() || !currentlyHandlingPlayStateChange) {
settingsUpdatedExplicitly = true;
}
setShowAddedScores(newSettings.isShowAddedScores());
List<ExpandableSortableColumn<?>> columnsToExpandAgain = new ArrayList<ExpandableSortableColumn<?>>();
final List<ExpandableSortableColumn<?>> columnsToExpandAgain = new ArrayList<ExpandableSortableColumn<?>>();
for (int i = 0; i < getLeaderboardTable().getColumnCount(); i++) {
Column<LeaderboardRowDTO, ?> c = getLeaderboardTable().getColumn(i);
if (c instanceof ExpandableSortableColumn<?>) {
@@ -366,10 +371,6 @@ public class LeaderboardPanel extends SimplePanel implements TimeListener, PlayS
selectedRaceDetails.clear();
selectedRaceDetails.addAll(newSettings.getRaceDetailsToShow());
}
if (newSettings.getOverallDetailsToShow() != null) {
selectedOverallDetailColumns.clear();
selectedOverallDetailColumns.addAll(newSettings.getOverallDetailsToShow());
}
// update strategy for determining the race columns to show; if settings' race columns to show is null, use the
// previously selected race columns / number of race columns for the new configuration
Iterable<String> oldNamesOfRaceColumnsToShow = null;
@@ -414,26 +415,66 @@ public class LeaderboardPanel extends SimplePanel implements TimeListener, PlayS
setRaceColumnSelectionToLastNStrategy(newSettings.getNumberOfLastRacesToShow());
break;
}
setAutoExpandPreSelectedRace(false); // avoid expansion during updateLeaderboard(...); will expand later if it
// was expanded before
// update leaderboard after settings panel column selection change
updateLeaderboard(leaderboard);
setAutoExpandPreSelectedRace(newSettings.isAutoExpandPreSelectedRace());
final boolean oldBusyState = getBusyIndicator().isBusy();
getBusyIndicator().setBusy(true);
Runnable doWhenNecessaryDetailHasBeenLoaded = new Runnable() {
@Override
public void run() {
setAutoExpandPreSelectedRace(false); // avoid expansion during updateLeaderboard(...); will expand later
// if it was expanded before
// update leaderboard after settings panel column selection change
updateLeaderboard(leaderboard);
setAutoExpandPreSelectedRace(newSettings.isAutoExpandPreSelectedRace());
if (newSettings.getDelayBetweenAutoAdvancesInMilliseconds() != null) {
timer.setRefreshInterval(newSettings.getDelayBetweenAutoAdvancesInMilliseconds());
}
for (ExpandableSortableColumn<?> expandableSortableColumn : columnsToExpandAgain) {
expandableSortableColumn.toggleExpansion();
}
if (newSettings.getNameOfRaceToSort() != null) {
final RaceColumn<?> raceColumnByRaceName = getRaceColumnByRaceName(newSettings.getNameOfRaceToSort());
if (raceColumnByRaceName != null) {
getLeaderboardTable().sortColumn(raceColumnByRaceName, /* ascending */true);
if (newSettings.getDelayBetweenAutoAdvancesInMilliseconds() != null) {
timer.setRefreshInterval(newSettings.getDelayBetweenAutoAdvancesInMilliseconds());
}
for (ExpandableSortableColumn<?> expandableSortableColumn : columnsToExpandAgain) {
expandableSortableColumn.toggleExpansion();
}
if (newSettings.getNameOfRaceToSort() != null) {
final RaceColumn<?> raceColumnByRaceName = getRaceColumnByRaceName(newSettings
.getNameOfRaceToSort());
if (raceColumnByRaceName != null) {
getLeaderboardTable().sortColumn(raceColumnByRaceName, /* ascending */true);
}
}
getBusyIndicator().setBusy(oldBusyState);
}
};
if (oldShallAddOverallDetails == shallAddOverallDetails() || oldShallAddOverallDetails || getLeaderboard().hasOverallDetails()) {
doWhenNecessaryDetailHasBeenLoaded.run();
} else { // meaning that now the details need to be loaded from the server
updateLeaderboardAndRun(doWhenNecessaryDetailHasBeenLoaded);
}
}
/**
* @param callWhenExpansionDataIsLoaded
*/
private void updateLeaderboardAndRun(final Runnable callWhenExpansionDataIsLoaded) {
final LeaderboardDTO previousLeaderboard = getLeaderboard();
getSailingService().getLeaderboardByName(getLeaderboardName(),
timer.getPlayMode() == PlayModes.Live ? null : getLeaderboardDisplayDate(),
/* namesOfRacesForWhichToLoadLegDetails */getNamesOfExpandedRaces(),
shallAddOverallDetails(), previousLeaderboard.getId(), new MarkedAsyncCallback<IncrementalOrFullLeaderboardDTO>(
new AsyncCallback<IncrementalOrFullLeaderboardDTO>() {
@Override
public void onSuccess(IncrementalOrFullLeaderboardDTO result) {
updateLeaderboard(result.getLeaderboardDTO(previousLeaderboard));
callWhenExpansionDataIsLoaded.run();
}
@Override
public void onFailure(Throwable caught) {
getErrorReporter().reportError(
stringMessages.errorTryingToObtainLeaderboardContents(caught.getMessage()),
true /* silentMode */);
}
}));
}
private void setRaceColumnSelectionToLastNStrategy(final Integer numberOfLastRacesToShow) {
raceColumnSelection = new LastNRacesColumnSelection(numberOfLastRacesToShow, getRaceTimesInfoProvider());
if (timer.getPlayState() != Timer.PlayStates.Playing) {
@@ -788,25 +829,7 @@ public class LeaderboardPanel extends SimplePanel implements TimeListener, PlayS
if (getLeaderboard().getLegCount(getRaceColumnName()) != -1) {
callWhenExpansionDataIsLoaded.run();
} else {
final LeaderboardDTO previousLeaderboard = getLeaderboard();
getSailingService().getLeaderboardByName(getLeaderboardName(),
timer.getPlayMode() == PlayModes.Live ? null : getLeaderboardDisplayDate(),
/* namesOfRacesForWhichToLoadLegDetails */getNamesOfExpandedRaces(),
shallAddOverallDetails(), previousLeaderboard.getId(), new MarkedAsyncCallback<IncrementalOrFullLeaderboardDTO>(
new AsyncCallback<IncrementalOrFullLeaderboardDTO>() {
@Override
public void onSuccess(IncrementalOrFullLeaderboardDTO result) {
updateLeaderboard(result.getLeaderboardDTO(previousLeaderboard));
callWhenExpansionDataIsLoaded.run();
}
@Override
public void onFailure(Throwable caught) {
getErrorReporter().reportError(
stringMessages.errorTryingToObtainLeaderboardContents(caught.getMessage()),
true /* silentMode */);
}
}));
updateLeaderboardAndRun(callWhenExpansionDataIsLoaded);
}
}
@@ -61,6 +61,7 @@ import com.sap.sailing.domain.base.Course;
import com.sap.sailing.domain.base.CourseArea;
import com.sap.sailing.domain.base.CourseBase;
import com.sap.sailing.domain.base.Event;
import com.sap.sailing.domain.base.EventBase;
import com.sap.sailing.domain.base.Fleet;
import com.sap.sailing.domain.base.Mark;
import com.sap.sailing.domain.base.Nationality;
@@ -68,6 +69,7 @@ import com.sap.sailing.domain.base.RaceColumn;
import com.sap.sailing.domain.base.RaceColumnInSeries;
import com.sap.sailing.domain.base.RaceDefinition;
import com.sap.sailing.domain.base.Regatta;
import com.sap.sailing.domain.base.RemoteSailingServerReference;
import com.sap.sailing.domain.base.Series;
import com.sap.sailing.domain.base.Sideline;
import com.sap.sailing.domain.base.Waypoint;
@@ -286,6 +288,7 @@ import com.sap.sailing.gwt.ui.shared.RegattaDTO;
import com.sap.sailing.gwt.ui.shared.RegattaOverviewEntryDTO;
import com.sap.sailing.gwt.ui.shared.RegattaScoreCorrectionDTO;
import com.sap.sailing.gwt.ui.shared.RegattaScoreCorrectionDTO.ScoreCorrectionEntryDTO;
import com.sap.sailing.gwt.ui.shared.RemoteSailingServerReferenceDTO;
import com.sap.sailing.gwt.ui.shared.ReplicaDTO;
import com.sap.sailing.gwt.ui.shared.ReplicationMasterDTO;
import com.sap.sailing.gwt.ui.shared.ReplicationStateDTO;
@@ -318,6 +321,7 @@ import com.sap.sailing.server.masterdata.MasterDataImporter;
import com.sap.sailing.server.operationaltransformation.AddColumnToLeaderboard;
import com.sap.sailing.server.operationaltransformation.AddColumnToSeries;
import com.sap.sailing.server.operationaltransformation.AddCourseArea;
import com.sap.sailing.server.operationaltransformation.AddRemoteSailingServerReference;
import com.sap.sailing.server.operationaltransformation.AddSpecificRegatta;
import com.sap.sailing.server.operationaltransformation.AllowCompetitorResetToDefaults;
import com.sap.sailing.server.operationaltransformation.ConnectTrackedRaceToLeaderboardColumn;
@@ -337,6 +341,7 @@ import com.sap.sailing.server.operationaltransformation.RemoveLeaderboard;
import com.sap.sailing.server.operationaltransformation.RemoveLeaderboardColumn;
import com.sap.sailing.server.operationaltransformation.RemoveLeaderboardGroup;
import com.sap.sailing.server.operationaltransformation.RemoveRegatta;
import com.sap.sailing.server.operationaltransformation.RemoveRemoteSailingServerReference;
import com.sap.sailing.server.operationaltransformation.RemoveSeries;
import com.sap.sailing.server.operationaltransformation.RenameEvent;
import com.sap.sailing.server.operationaltransformation.RenameLeaderboard;
@@ -391,7 +396,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
private final ServiceTracker<TracTracAdapterFactory, TracTracAdapterFactory> tractracAdapterTracker;
private final ServiceTracker<IgtimiConnectionFactory, IgtimiConnectionFactory> igtimiAdapterTracker;
private final ServiceTracker<RaceLogTrackingAdapterFactory, RaceLogTrackingAdapterFactory> raceLogTrackingAdapterTracker;
private final ServiceTracker<DeviceIdentifierStringSerializationHandler, DeviceIdentifierStringSerializationHandler>
@@ -2226,8 +2231,8 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
}
@Override
public Void updateLeaderboardScoreCorrectionMetadata(String leaderboardName, Date timePointOfLastCorrectionValidity, String comment) {
return getService().apply(
public void updateLeaderboardScoreCorrectionMetadata(String leaderboardName, Date timePointOfLastCorrectionValidity, String comment) {
getService().apply(
new UpdateLeaderboardScoreCorrectionMetadata(leaderboardName,
timePointOfLastCorrectionValidity == null ? null : new MillisecondsTimePoint(timePointOfLastCorrectionValidity),
comment));
@@ -2949,6 +2954,48 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
return result;
}
@Override
public List<RemoteSailingServerReferenceDTO> getPublicEventsOfAllSailingServers() {
List<RemoteSailingServerReferenceDTO> result = new ArrayList<RemoteSailingServerReferenceDTO>();
for (Entry<RemoteSailingServerReference, Pair<Iterable<EventBase>, Exception>> serverRefAndEventsOrException :
getService().getPublicEventsOfAllSailingServers().entrySet()) {
final Pair<Iterable<EventBase>, Exception> eventsOrException = serverRefAndEventsOrException.getValue();
final RemoteSailingServerReference serverRef = serverRefAndEventsOrException.getKey();
final RemoteSailingServerReferenceDTO sailingServerDTO = createRemoteSailingServerReferenceDTO(serverRef, eventsOrException);
result.add(sailingServerDTO);
}
return result;
}
private RemoteSailingServerReferenceDTO createRemoteSailingServerReferenceDTO(
final RemoteSailingServerReference serverRef,
final Pair<Iterable<EventBase>, Exception> eventsOrException) {
final Iterable<EventBase> events = eventsOrException.getA();
final Iterable<EventDTO> eventDTOs;
final RemoteSailingServerReferenceDTO sailingServerDTO;
if (events == null) {
eventDTOs = null;
final Exception exception = eventsOrException.getB();
sailingServerDTO = new RemoteSailingServerReferenceDTO(serverRef.getName(),
serverRef.getURL().toExternalForm(), exception==null?null:exception.getMessage());
} else {
eventDTOs = convertToEventDTOs(events);
sailingServerDTO = new RemoteSailingServerReferenceDTO(
serverRef.getName(), serverRef
.getURL().toExternalForm(), eventDTOs);
}
return sailingServerDTO;
}
private Iterable<EventDTO> convertToEventDTOs(Iterable<EventBase> events) {
List<EventDTO> result = new ArrayList<>();
for (EventBase event : events) {
EventDTO eventDTO = convertToEventDTO(event);
result.add(eventDTO);
}
return result;
}
@Override
public void updateEvent(UUID eventId, String eventName, Date startDate, Date endDate, VenueDTO venue, boolean isPublic, List<String> regattaNames) {
TimePoint startTimePoint = startDate != null ? new MillisecondsTimePoint(startDate) : null;
@@ -3013,6 +3060,17 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
return result;
}
private EventDTO convertToEventDTO(EventBase event) {
EventDTO eventDTO = new EventDTO(event.getName());
eventDTO.venue = new VenueDTO();
eventDTO.venue.setName(event.getVenue() != null ? event.getVenue().getName() : null);
eventDTO.startDate = event.getStartDate() != null ? event.getStartDate().asDate() : null;
eventDTO.endDate = event.getStartDate() != null ? event.getEndDate().asDate() : null;
eventDTO.isPublic = event.isPublic();
eventDTO.id = (UUID) event.getId();
return eventDTO;
}
private EventDTO convertToEventDTO(Event event) {
EventDTO eventDTO = new EventDTO(event.getName());
eventDTO.venue = new VenueDTO();
@@ -3256,6 +3314,41 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
return result;
}
@Override
public List<RemoteSailingServerReferenceDTO> getRemoteSailingServerReferences() {
List<RemoteSailingServerReferenceDTO> result = new ArrayList<RemoteSailingServerReferenceDTO>();
for (Entry<RemoteSailingServerReference, Pair<Iterable<EventBase>, Exception>> remoteSailingServerRefAndItsCachedEvent :
getService().getPublicEventsOfAllSailingServers().entrySet()) {
RemoteSailingServerReferenceDTO dto = createRemoteSailingServerReferenceDTO(
remoteSailingServerRefAndItsCachedEvent.getKey(),
remoteSailingServerRefAndItsCachedEvent.getValue());
result.add(dto);
}
return result;
}
@Override
public void removeSailingServers(Set<String> namesOfSailingServersToRemove) throws Exception {
for (String serverName : namesOfSailingServersToRemove) {
getService().apply(new RemoveRemoteSailingServerReference(serverName));
}
}
@Override
public RemoteSailingServerReferenceDTO addRemoteSailingServerReference(RemoteSailingServerReferenceDTO sailingServer) throws MalformedURLException {
final String expandedURL;
if (sailingServer.getUrl().contains("//")) {
expandedURL = sailingServer.getUrl();
} else {
expandedURL = "http://" + sailingServer.getUrl();
}
URL serverURL = new URL(expandedURL);
RemoteSailingServerReference serverRef = getService().apply(new AddRemoteSailingServerReference(sailingServer.getName(), serverURL));
Pair<Iterable<EventBase>, Exception> eventsOrException = getService().updateRemoteServerEventCacheSynchronously(serverRef);
return createRemoteSailingServerReferenceDTO(serverRef, eventsOrException);
}
@Override
public List<String> getResultImportUrls(String resultProviderName) {
List<String> result = new ArrayList<String>();
@@ -3781,7 +3874,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
return convertToCompetitorDTOs(leaderboard.getAllCompetitors());
}
}
@Override
public CompetitorDTO addOrUpdateCompetitor(CompetitorDTO competitor) {
Nationality nationality = (competitor.getThreeLetterIocCountryCode() == null || competitor.getThreeLetterIocCountryCode().isEmpty()) ? null :
@@ -4084,7 +4177,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
}
}
@Override
public void denoteForRaceLogTracking(String leaderboardName,
String raceColumnName, String fleetName) throws Exception {
@@ -4431,4 +4524,4 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
getRaceLogTrackingAdapter().startTracking(getService(), leaderboard, raceColumn, fleet);
}
}
}
@@ -0,0 +1,58 @@
package com.sap.sailing.gwt.ui.shared;
import java.util.Collections;
import com.google.gwt.user.client.rpc.IsSerializable;
import com.sap.sailing.domain.common.dto.NamedDTO;
public class RemoteSailingServerReferenceDTO extends NamedDTO implements IsSerializable {
private static final long serialVersionUID = -4209262742778693873L;
private String url;
private Iterable<EventDTO> events;
/**
* The error message is usually filled in case {@link #events} is <code>null</code> and gives a hint about the
* server-side error that occurred trying to obtain the event list from the server identified by the reference
* represented by this DTO.
*/
private String lastErrorMessage;
// for GWT
RemoteSailingServerReferenceDTO() {
}
public RemoteSailingServerReferenceDTO(String name, String url) {
this(name, url, Collections. <EventDTO> emptyList());
}
public RemoteSailingServerReferenceDTO(String name, String url, Iterable<EventDTO> events) {
this(name, url, events, /* error message */ null);
}
public RemoteSailingServerReferenceDTO(String name, String url, Iterable<EventDTO> events, String lastErrorMessage) {
super(name);
this.url = url;
this.events = events;
this.lastErrorMessage = lastErrorMessage;
}
public RemoteSailingServerReferenceDTO(String name, String url, String lastErrorMessage) {
this(name, url, /* events */ null, lastErrorMessage);
}
public String getUrl() {
return url;
}
public Iterable<EventDTO> getEvents() {
return events;
}
public String getLastErrorMessage() {
return lastErrorMessage;
}
public void setLastErrorMessage(String lastErrorMessage) {
this.lastErrorMessage = lastErrorMessage;
}
}
@@ -34,7 +34,9 @@ public class EventsResource extends AbstractSailingServerResource {
JsonSerializer<EventBase> eventSerializer = new EventJsonSerializer(new VenueJsonSerializer(new CourseAreaJsonSerializer()));
JSONArray result = new JSONArray();
for (EventBase event : getService().getAllEvents()) {
result.add(eventSerializer.serialize(event));
if (event.isPublic()) {
result.add(eventSerializer.serialize(event));
}
}
String json = result.toJSONString();
return Response.ok(json, MediaType.APPLICATION_JSON).build();
@@ -47,16 +49,16 @@ public class EventsResource extends AbstractSailingServerResource {
Response response;
UUID eventUuid;
try {
eventUuid = UUID.fromString(eventId);
} catch (IllegalArgumentException e) {
return getBadEventErrorResponse(eventId);
}
eventUuid = UUID.fromString(eventId);
} catch (IllegalArgumentException e) {
return getBadEventErrorResponse(eventId);
}
Event event = getService().getEvent(eventUuid);
if (event == null) {
response = getBadEventErrorResponse(eventId);
} else {
JsonSerializer<EventBase> eventSerializer = new EventJsonSerializer(new VenueJsonSerializer(new CourseAreaJsonSerializer()));
JsonSerializer<EventBase> eventSerializer = new EventJsonSerializer(new VenueJsonSerializer(
new CourseAreaJsonSerializer()));
JSONObject eventJson = eventSerializer.serialize(event);
String json = eventJson.toJSONString();
@@ -64,7 +66,7 @@ public class EventsResource extends AbstractSailingServerResource {
}
return response;
}
@GET
@Produces("application/json;charset=UTF-8")
@Path("{eventId}/racestates")
File diff suppressed because it is too large. Load diff
@@ -6,6 +6,7 @@ import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.net.MalformedURLException;
import java.net.SocketException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@@ -36,6 +37,7 @@ import com.sap.sailing.domain.base.ControlPoint;
import com.sap.sailing.domain.base.CourseArea;
import com.sap.sailing.domain.base.DomainFactory;
import com.sap.sailing.domain.base.Event;
import com.sap.sailing.domain.base.EventBase;
import com.sap.sailing.domain.base.Fleet;
import com.sap.sailing.domain.base.Mark;
import com.sap.sailing.domain.base.RaceColumn;
@@ -43,6 +45,7 @@ import com.sap.sailing.domain.base.RaceColumnInSeries;
import com.sap.sailing.domain.base.RaceDefinition;
import com.sap.sailing.domain.base.Regatta;
import com.sap.sailing.domain.base.RegattaListener;
import com.sap.sailing.domain.base.RemoteSailingServerReference;
import com.sap.sailing.domain.base.Series;
import com.sap.sailing.domain.base.Sideline;
import com.sap.sailing.domain.base.Waypoint;
@@ -54,6 +57,7 @@ import com.sap.sailing.domain.base.configuration.impl.DeviceConfigurationMapImpl
import com.sap.sailing.domain.base.impl.DynamicCompetitor;
import com.sap.sailing.domain.base.impl.EventImpl;
import com.sap.sailing.domain.base.impl.RegattaImpl;
import com.sap.sailing.domain.base.impl.RemoteSailingServerReferenceImpl;
import com.sap.sailing.domain.common.DataImportProgress;
import com.sap.sailing.domain.common.LeaderboardNameConstants;
import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
@@ -186,8 +190,10 @@ public class RacingEventServiceImpl implements RacingEventServiceWithTestSupport
* Holds the {@link Event} objects for those event registered with this service. Note that there may be
* {@link Event} objects that exist outside this service for events not (yet) registered here.
*/
protected final ConcurrentHashMap<Serializable, Event> eventsById;
private final ConcurrentHashMap<Serializable, Event> eventsById;
private final RemoteSailingServerSet remoteSailingServerSet;
/**
* Holds the {@link Regatta} objects for those races registered with this service. Note that there may be
* {@link Regatta} objects that exist outside this service for regattas not (yet) registered here.
@@ -308,7 +314,7 @@ public class RacingEventServiceImpl implements RacingEventServiceWithTestSupport
* object so that service lookups become possible.
*/
private BundleContext bundleContext;
private TypeBasedServiceFinderFactory serviceFinderFactory;
/**
@@ -390,6 +396,7 @@ public class RacingEventServiceImpl implements RacingEventServiceWithTestSupport
this.windStore = windStore;
this.dataImportLock = new DataImportLockWithProgress();
remoteSailingServerSet = new RemoteSailingServerSet(scheduler);
regattasByName = new ConcurrentHashMap<String, Regatta>();
regattasByNameLock = new NamedReentrantReadWriteLock("regattasByName for "+this, /* fair */ false);
eventsById = new ConcurrentHashMap<Serializable, Event>();
@@ -419,6 +426,7 @@ public class RacingEventServiceImpl implements RacingEventServiceWithTestSupport
}
this.gpsFixStore = gpsFixStore;
this.configurationMap = new DeviceConfigurationMapImpl();
this.serviceFinderFactory = serviceFinderFactory;
// Add one default leaderboard that aggregates all races currently tracked by this service.
// This is more for debugging purposes than for anything else.
@@ -430,7 +438,7 @@ public class RacingEventServiceImpl implements RacingEventServiceWithTestSupport
loadStoredLeaderboardsAndGroups();
loadMediaLibary();
loadStoredDeviceConfigurations();
this.serviceFinderFactory = serviceFinderFactory;
loadAllRemoteSailingServersAndSchedulePeriodicEventCacheRefresh();
}
@Override
@@ -498,7 +506,13 @@ public class RacingEventServiceImpl implements RacingEventServiceWithTestSupport
eventsById.put(event.getId(), event);
}
}
private void loadAllRemoteSailingServersAndSchedulePeriodicEventCacheRefresh() {
for (RemoteSailingServerReference sailingServer : domainObjectFactory.loadAllRemoteSailingServerReferences()) {
remoteSailingServerSet.add(sailingServer);
}
}
/**
* Collects media track references from the configured sources (mongo DB by default, ftp folder yet to be
* implemented). The method is expected to be called initially blocking the API until finished.
@@ -846,6 +860,30 @@ public class RacingEventServiceImpl implements RacingEventServiceWithTestSupport
return Collections.unmodifiableMap(new HashMap<String, Leaderboard>(leaderboardsByName));
}
@Override
public Map<RemoteSailingServerReference, Pair<Iterable<EventBase>, Exception>> getPublicEventsOfAllSailingServers() {
return remoteSailingServerSet.getCachedEventsForRemoteSailingServers();
}
@Override
public RemoteSailingServerReference addRemoteSailingServerReference(String name, URL url) {
RemoteSailingServerReference result = new RemoteSailingServerReferenceImpl(name, url);
remoteSailingServerSet.add(result);
mongoObjectFactory.storeSailingServer(result);
return result;
}
@Override
public Pair<Iterable<EventBase>, Exception> updateRemoteServerEventCacheSynchronously(RemoteSailingServerReference ref) {
return remoteSailingServerSet.getEventsOrException(ref);
}
@Override
public void removeRemoteSailingServerReference(String name) {
remoteSailingServerSet.remove(name);
mongoObjectFactory.removeSailingServer(name);
}
@Override
public Iterable<Event> getAllEvents() {
return Collections.unmodifiableCollection(new ArrayList<Event>(eventsById.values()));
@@ -1967,7 +2005,6 @@ public class RacingEventServiceImpl implements RacingEventServiceWithTestSupport
logger.info("Serializing persisted competitors...");
oos.writeObject(competitorStore);
logoutput.append("Serialized " + competitorStore.size() + " persisted competitors\n");
logger.info(logoutput.toString());
logger.info("Serializing configuration map...");
oos.writeObject(configurationMap);
@@ -1976,7 +2013,13 @@ public class RacingEventServiceImpl implements RacingEventServiceWithTestSupport
logoutput.append(String.format("%3s\n", matcher.toString()));
}
logger.info(logoutput.toString()); }
logger.info("Serializing remote sailing server references...");
final ArrayList<RemoteSailingServerReference> remoteServerReferences = new ArrayList<>(remoteSailingServerSet.getCachedEventsForRemoteSailingServers().keySet());
oos.writeObject(remoteServerReferences);
logoutput.append("Serialized "+remoteServerReferences.size()+" remote sailing server references\n");
logger.info(logoutput.toString());
}
@SuppressWarnings("unchecked")
// the type-parameters in the casts of the de-serialized collection objects can't be checked
@@ -2034,6 +2077,7 @@ public class RacingEventServiceImpl implements RacingEventServiceWithTestSupport
eventsById.clear();
mediaLibrary.clear();
competitorStore.clear();
remoteSailingServerSet.clear();
StringBuffer logoutput = new StringBuffer();
@@ -2091,7 +2135,6 @@ public class RacingEventServiceImpl implements RacingEventServiceWithTestSupport
logoutput.append(String.format("%3s\n", mediatrack.toString()));
}
// only copy the competitors from the deserialized competitor store; don't use it because it will have set
// a default Mongo object factory
logger.info("Reading competitors...");
@@ -2107,6 +2150,13 @@ public class RacingEventServiceImpl implements RacingEventServiceWithTestSupport
for (DeviceConfigurationMatcher matcher : configurationMap.keySet()) {
logoutput.append(String.format("%3s\n", matcher.toString()));
}
logger.info("Reading remote sailing server references...");
for (RemoteSailingServerReference remoteSailingServerReference : (Iterable<RemoteSailingServerReference>) ois.readObject()) {
remoteSailingServerSet.add(remoteSailingServerReference);
logoutput.append("Received remote sailing server reference "+remoteSailingServerReference);
}
logger.info(logoutput.toString());
} finally {
Thread.currentThread().setContextClassLoader(oldContextClassloader);
@@ -0,0 +1,150 @@
package com.sap.sailing.server.impl;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
import com.sap.sailing.domain.base.DomainFactory;
import com.sap.sailing.domain.base.Event;
import com.sap.sailing.domain.base.EventBase;
import com.sap.sailing.domain.base.RemoteSailingServerReference;
import com.sap.sailing.domain.common.impl.Util.Pair;
import com.sap.sailing.server.gateway.deserialization.impl.CourseAreaJsonDeserializer;
import com.sap.sailing.server.gateway.deserialization.impl.EventBaseJsonDeserializer;
import com.sap.sailing.server.gateway.deserialization.impl.VenueJsonDeserializer;
/**
* A set of {@link RemoteSailingServerReference}s including a cache of their {@link Event}s that is
* periodically updated.
*
* @author Axel Uhl (D043530)
*
*/
public class RemoteSailingServerSet {
private static final Logger logger = Logger.getLogger(RemoteSailingServerSet.class.getName());
/**
* Holds the {@link Event} objects for the events of all registered sailing server instances.
*/
private final ConcurrentHashMap<String, RemoteSailingServerReference> remoteSailingServers;
private final ConcurrentHashMap<RemoteSailingServerReference, Pair<Iterable<EventBase>, Exception>> cachedEventsForRemoteSailingServers;
/**
* @param scheduler
* Used to schedule the periodic updates of the {@link #cachedEventsForRemoteSailingServers event cache}
*/
public RemoteSailingServerSet(ScheduledExecutorService scheduler) {
remoteSailingServers = new ConcurrentHashMap<>();
cachedEventsForRemoteSailingServers = new ConcurrentHashMap<>();
scheduler.scheduleAtFixedRate(new Runnable() { @Override public void run() { updateRemoteSailingServerReferenceEventCaches(); } },
/* initialDelay */ 0, /* period */ 60, TimeUnit.SECONDS);
}
public void clear() {
remoteSailingServers.clear();
cachedEventsForRemoteSailingServers.clear();
}
public void add(RemoteSailingServerReference remoteSailingServerReference) {
remoteSailingServers.put(remoteSailingServerReference.getName(), remoteSailingServerReference);
triggerAsynchronousEventCacheUpdate(remoteSailingServerReference);
}
private void updateRemoteSailingServerReferenceEventCaches() {
for (RemoteSailingServerReference ref : remoteSailingServers.values()) {
triggerAsynchronousEventCacheUpdate(ref);
}
}
private void triggerAsynchronousEventCacheUpdate(final RemoteSailingServerReference ref) {
new Thread("Event Cache Updater for remote server "+ref) {
@Override public void run() { updateRemoteServerEventCacheSynchronously(ref); }
}.start();
}
private Pair<Iterable<EventBase>, Exception> updateRemoteServerEventCacheSynchronously(RemoteSailingServerReference ref) {
BufferedReader bufferedReader = null;
Pair<Iterable<EventBase>, Exception> result;
try {
try {
final URL eventsURL = getEventsURL(ref.getURL());
logger.fine("Updating events for remote server "+ref+" from URL "+eventsURL);
URLConnection urlConnection = eventsURL.openConnection();
urlConnection.connect();
bufferedReader = new BufferedReader(new InputStreamReader(urlConnection.getInputStream()));
JSONParser parser = new JSONParser();
Object eventsAsObject = parser.parse(bufferedReader);
EventBaseJsonDeserializer deserializer = new EventBaseJsonDeserializer(new VenueJsonDeserializer(
new CourseAreaJsonDeserializer(DomainFactory.INSTANCE)));
JSONArray eventsAsJsonArray = (JSONArray) eventsAsObject;
final Set<EventBase> events = new HashSet<>();
for (Object eventAsObject : eventsAsJsonArray) {
JSONObject eventAsJson = (JSONObject) eventAsObject;
EventBase event = deserializer.deserialize(eventAsJson);
events.add(event);
}
result = new Pair<Iterable<EventBase>, Exception>(events, /* exception */ null);
} finally {
if (bufferedReader != null) {
bufferedReader.close();
}
}
} catch (IOException | ParseException e) {
logger.log(Level.INFO, "Exception trying to fetch events from remote server "+ref+": "+e.getMessage(), e);
result = new Pair<Iterable<EventBase>, Exception>(/* events */ null, e);
}
cachedEventsForRemoteSailingServers.put(ref, result);
return result;
}
private URL getEventsURL(URL remoteServerBaseURL) throws MalformedURLException {
String getEventsUrl = remoteServerBaseURL.toExternalForm();
if (!getEventsUrl.endsWith("/")) {
getEventsUrl += "/";
}
getEventsUrl += "sailingserver/api/v1/events";
return new URL(getEventsUrl);
}
public Map<RemoteSailingServerReference, Pair<Iterable<EventBase>, Exception>> getCachedEventsForRemoteSailingServers() {
return Collections.unmodifiableMap(cachedEventsForRemoteSailingServers);
}
public RemoteSailingServerReference remove(String name) {
RemoteSailingServerReference ref = remoteSailingServers.remove(name);
if (ref != null) {
cachedEventsForRemoteSailingServers.remove(ref);
}
return ref;
}
/**
* Synchronously fetches the latest events list for the remote server reference specified. The
* result is cached. If <code>ref</code> was not yet part of this remote sailing server reference set,
* it is automatically added.
*/
public Pair<Iterable<EventBase>, Exception> getEventsOrException(RemoteSailingServerReference ref) {
if (!remoteSailingServers.containsKey(ref.getName())) {
remoteSailingServers.put(ref.getName(), ref);
}
return updateRemoteServerEventCacheSynchronously(ref);
}
}
@@ -0,0 +1,46 @@
package com.sap.sailing.server.operationaltransformation;
import java.net.URL;
import com.sap.sailing.domain.base.RemoteSailingServerReference;
import com.sap.sailing.server.RacingEventService;
import com.sap.sailing.server.RacingEventServiceOperation;
/**
* Adds a {@link RemoteSailingServerReference} instance.
*
* @author Frank Mittag (c5163874)
*
*/
public class AddRemoteSailingServerReference extends AbstractRacingEventServiceOperation<RemoteSailingServerReference> {
private static final long serialVersionUID = -226327171479379243L;
private final String serverName;
private final URL serverUrl;
public AddRemoteSailingServerReference(String serverName, URL serverUrl) {
super();
this.serverName = serverName;
this.serverUrl = serverUrl;
}
@Override
public RemoteSailingServerReference internalApplyTo(RacingEventService toState) throws Exception {
RemoteSailingServerReference result = toState.addRemoteSailingServerReference(serverName, serverUrl);
return result;
}
@Override
public RacingEventServiceOperation<?> transformClientOp(
RacingEventServiceOperation<?> serverOp) {
// TODO Auto-generated method stub
return null;
}
@Override
public RacingEventServiceOperation<?> transformServerOp(
RacingEventServiceOperation<?> clientOp) {
// TODO Auto-generated method stub
return null;
}
}
@@ -0,0 +1,42 @@
package com.sap.sailing.server.operationaltransformation;
import com.sap.sailing.domain.base.RemoteSailingServerReference;
import com.sap.sailing.server.RacingEventService;
import com.sap.sailing.server.RacingEventServiceOperation;
/**
* Removes a {@link RemoteSailingServerReference} instance.
*
* @author Frank Mittag (c5163874)
*
*/
public class RemoveRemoteSailingServerReference extends AbstractRacingEventServiceOperation<Void> {
private static final long serialVersionUID = -1283971355735610505L;
private final String serverName;
public RemoveRemoteSailingServerReference(String serverName) {
super();
this.serverName = serverName;
}
@Override
public Void internalApplyTo(RacingEventService toState) throws Exception {
toState.removeRemoteSailingServerReference(serverName);
return null;
}
@Override
public RacingEventServiceOperation<?> transformClientOp(
RacingEventServiceOperation<?> serverOp) {
// TODO Auto-generated method stub
return null;
}
@Override
public RacingEventServiceOperation<?> transformServerOp(
RacingEventServiceOperation<?> clientOp) {
// TODO Auto-generated method stub
return null;
}
}
@@ -38,6 +38,10 @@
<h2 class="articleSubheadline">May 2014</h2>
<ul class="bulletList">
<li>Fixed an issue with the wind speed charts. Charts that only had a single, unchanged
wind speed crashed the browser. The solution scales the right hand side Y-axis denoting
the wind speed such that it always starts at 0kts.
</li>
<li>Analysis of tacks and jibes around mark passings has now been improved. Before,
a jibe followed by a tack that turns in the same direction (e.g., to port) so far
was always called a penalty maneuver. Now, if a mark passing separates such a tack
+9 -1
View File
@@ -231,4 +231,12 @@ When installing a new version of server code that contains changes related to GW
<pre>
-Dorg.eclipse.jetty.LEVEL=OFF -Dorg.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
</pre>
</pre>
### Review branch in eclipse
Before merging into master in some cases you want to review a branch. The easiest way to do this is to use EGit from within Eclipse. Here are the steps based on reviewing branch tracapi. This tutorial assumes that you have checked out the current master branch!
- Open "GIT Repository Exploring" and select the branch that you want to review. Right click on that branch and select "Synchronize with Workspace".
- A new view ("Team Synchronizing") will open and show you the changes on the left side. For each file you can now check for changes. Sometimes there will be resources that you do not want to review. In that case simply select a resource and hit "Remove from view".