069b69eeed04755abfb97bd6f14c4df3105c7be0
build.gradle
| ... | ... | @@ -13,7 +13,7 @@ buildscript { |
| 13 | 13 | } |
| 14 | 14 | } |
| 15 | 15 | dependencies { |
| 16 | - classpath 'com.android.tools.build:gradle:3.6.0' |
|
| 16 | + classpath 'com.android.tools.build:gradle:3.6.1' |
|
| 17 | 17 | classpath "com.getkeepsafe.dexcount:dexcount-gradle-plugin:0.8.3" |
| 18 | 18 | classpath "org.ajoberstar:grgit:2.3.0" |
| 19 | 19 | } |
gradle.properties
| ... | ... | @@ -21,8 +21,8 @@ org.gradle.jvmargs=-Xmx4096m |
| 21 | 21 | |
| 22 | 22 | commonRepoURL=http://nexus.wdf.sap.corp:8081/nexus/content/repositories/build.snapshots |
| 23 | 23 | sapGradleDistBaseUrl=http://nexus.wdf.sap.corp:8081/nexus/content/repositories/build.releases/org/gradle/download/gradle/gradle |
| 24 | -sapGradleVersion=4.6 |
|
| 24 | +sapGradleVersion=6.0.1 |
|
| 25 | 25 | sapProxyHost=proxy.wdf.sap.corp |
| 26 | 26 | sapProxyPort=8080 |
| 27 | 27 | sapNonProxyHosts=nexus.wdf.sap.corp |
| 28 | -group=com.sap.sailing.android |
|
| ... | ... | \ No newline at end of file |
| 0 | +group=com.sap.sailing.android |
gradle/wrapper/gradle-wrapper.properties
| ... | ... | @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME |
| 3 | 3 | distributionPath=wrapper/dists |
| 4 | 4 | zipStoreBase=GRADLE_USER_HOME |
| 5 | 5 | zipStorePath=wrapper/dists |
| 6 | -distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip |
|
| 6 | +distributionUrl=https\://services.gradle.org/distributions/gradle-6.0.1-all.zip |
java/com.sap.sailing.dashboards.gwt/META-INF/MANIFEST.MF
| ... | ... | @@ -23,7 +23,8 @@ Require-Bundle: com.sap.sailing.domain, |
| 23 | 23 | com.sap.sse.gwt.adminconsole, |
| 24 | 24 | org.apache.commons.math;bundle-version="2.1.0", |
| 25 | 25 | com.sap.sse.security.ui, |
| 26 | - com.sap.sse |
|
| 26 | + com.sap.sse, |
|
| 27 | + com.sap.sse.replication.interfaces |
|
| 27 | 28 | Bundle-ActivationPolicy: lazy |
| 28 | 29 | Import-Package: com.sap.sse.security.shared.dto, |
| 29 | 30 | javax.servlet;version="3.1.0", |
java/com.sap.sailing.dashboards.gwt/src/main/java/com/sap/sailing/dashboards/gwt/server/DashboardDispatchServlet.java
| ... | ... | @@ -15,31 +15,44 @@ import com.sap.sse.gwt.dispatch.client.transport.gwtrpc.RequestWrapper; |
| 15 | 15 | import com.sap.sse.gwt.dispatch.servlets.AbstractDispatchServlet; |
| 16 | 16 | import com.sap.sse.gwt.dispatch.shared.commands.Action; |
| 17 | 17 | import com.sap.sse.gwt.dispatch.shared.commands.Result; |
| 18 | +import com.sap.sse.replication.FullyInitializedReplicableTracker; |
|
| 19 | +import com.sap.sse.replication.ReplicationService; |
|
| 18 | 20 | import com.sap.sse.util.ServiceTrackerFactory; |
| 19 | 21 | |
| 20 | 22 | public class DashboardDispatchServlet extends AbstractDispatchServlet<DashboardDispatchContext> { |
| 21 | 23 | private static final long serialVersionUID = -245230476512348999L; |
| 22 | 24 | |
| 23 | - private final ServiceTracker<RacingEventService, RacingEventService> racingEventServiceTracker; |
|
| 25 | + private final FullyInitializedReplicableTracker<RacingEventService> racingEventServiceTracker; |
|
| 24 | 26 | private final PolarDataService polarDataService; |
| 25 | 27 | private final DashboardLiveRaceProvider dashboardLiveRaceProvider; |
| 26 | 28 | private final MovingAveragesCache movingAveragesCache; |
| 27 | 29 | |
| 28 | 30 | public DashboardDispatchServlet() { |
| 29 | 31 | final BundleContext context = Activator.getDefault(); |
| 30 | - racingEventServiceTracker = ServiceTrackerFactory.createAndOpen(context, RacingEventService.class); |
|
| 32 | + final ServiceTracker<ReplicationService, ReplicationService> replicationServiceTracker = |
|
| 33 | + ServiceTrackerFactory.createAndOpen(context, ReplicationService.class); |
|
| 34 | + racingEventServiceTracker = new FullyInitializedReplicableTracker<>(context, RacingEventService.class, |
|
| 35 | + /* customizer */ null, replicationServiceTracker); |
|
| 36 | + racingEventServiceTracker.open(); |
|
| 31 | 37 | polarDataService = getPolarDataService(); |
| 32 | - dashboardLiveRaceProvider = new DashboardLiveRaceProvider(racingEventServiceTracker.getService()); |
|
| 38 | + try { |
|
| 39 | + dashboardLiveRaceProvider = new DashboardLiveRaceProvider(racingEventServiceTracker.getInitializedService(0)); |
|
| 40 | + } catch (InterruptedException e) { |
|
| 41 | + throw new RuntimeException(e); |
|
| 42 | + } |
|
| 33 | 43 | movingAveragesCache = new MovingAveragesCache(); |
| 34 | 44 | } |
| 35 | 45 | |
| 36 | 46 | @Override |
| 37 | 47 | protected <R extends Result, A extends Action<R, DashboardDispatchContext>> DashboardDispatchContext createDispatchContextFor( |
| 38 | 48 | RequestWrapper<R, A, DashboardDispatchContext> request) { |
| 39 | - return new DashboardDispatchContextImpl(request.getCurrentClientTime(), |
|
| 40 | - racingEventServiceTracker.getService(), |
|
| 41 | - polarDataService, dashboardLiveRaceProvider, movingAveragesCache, request.getClientLocaleName(), |
|
| 42 | - getThreadLocalRequest()); |
|
| 49 | + try { |
|
| 50 | + return new DashboardDispatchContextImpl(request.getCurrentClientTime(), racingEventServiceTracker.getInitializedService(0), |
|
| 51 | + polarDataService, dashboardLiveRaceProvider, movingAveragesCache, request.getClientLocaleName(), |
|
| 52 | + getThreadLocalRequest()); |
|
| 53 | + } catch (InterruptedException e) { |
|
| 54 | + throw new RuntimeException(e); |
|
| 55 | + } |
|
| 43 | 56 | } |
| 44 | 57 | |
| 45 | 58 | private PolarDataService getPolarDataService() { |
java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/LegEntryDTO.java
| ... | ... | @@ -60,8 +60,18 @@ public class LegEntryDTO implements Serializable { |
| 60 | 60 | public boolean finished; |
| 61 | 61 | public Map<ManeuverType, Integer> numberOfManeuvers; |
| 62 | 62 | public Map<ManeuverType, Double> averageManeuverLossInMeters; |
| 63 | - public Double averageAbsoluteCrossTrackErrorInMeters; |
|
| 64 | - public Double averageSignedCrossTrackErrorInMeters; |
|
| 63 | + |
|
| 64 | + /** |
|
| 65 | + * The absolute cross track error; averaged when asked for a time point after the competitor has finished the leg, |
|
| 66 | + * or the current value when in the leg; {@code null} when not yet in the leg. |
|
| 67 | + */ |
|
| 68 | + public Double currentOrAverageAbsoluteCrossTrackErrorInMeters; |
|
| 69 | + |
|
| 70 | + /** |
|
| 71 | + * The signed cross track error; averaged when asked for a time point after the competitor has finished the leg, |
|
| 72 | + * or the current value when in the leg; {@code null} when not yet in the leg. |
|
| 73 | + */ |
|
| 74 | + public Double currentOrAverageSignedCrossTrackErrorInMeters; |
|
| 65 | 75 | |
| 66 | 76 | /** |
| 67 | 77 | * The corrected time spent since the start of the race up to the current time point or the finishing of |
| ... | ... | @@ -75,11 +85,11 @@ public class LegEntryDTO implements Serializable { |
| 75 | 85 | public int hashCode() { |
| 76 | 86 | final int prime = 31; |
| 77 | 87 | int result = 1; |
| 78 | - result = prime * result + ((averageAbsoluteCrossTrackErrorInMeters == null) ? 0 |
|
| 79 | - : averageAbsoluteCrossTrackErrorInMeters.hashCode()); |
|
| 88 | + result = prime * result + ((currentOrAverageAbsoluteCrossTrackErrorInMeters == null) ? 0 |
|
| 89 | + : currentOrAverageAbsoluteCrossTrackErrorInMeters.hashCode()); |
|
| 80 | 90 | result = prime * result + ((averageManeuverLossInMeters == null) ? 0 : averageManeuverLossInMeters.hashCode()); |
| 81 | - result = prime * result + ((averageSignedCrossTrackErrorInMeters == null) ? 0 |
|
| 82 | - : averageSignedCrossTrackErrorInMeters.hashCode()); |
|
| 91 | + result = prime * result + ((currentOrAverageSignedCrossTrackErrorInMeters == null) ? 0 |
|
| 92 | + : currentOrAverageSignedCrossTrackErrorInMeters.hashCode()); |
|
| 83 | 93 | result = prime * result |
| 84 | 94 | + ((averageSpeedOverGroundInKnots == null) ? 0 : averageSpeedOverGroundInKnots.hashCode()); |
| 85 | 95 | result = prime * result + ((correctedTotalTime == null) ? 0 : correctedTotalTime.hashCode()); |
| ... | ... | @@ -124,20 +134,20 @@ public class LegEntryDTO implements Serializable { |
| 124 | 134 | if (getClass() != obj.getClass()) |
| 125 | 135 | return false; |
| 126 | 136 | LegEntryDTO other = (LegEntryDTO) obj; |
| 127 | - if (averageAbsoluteCrossTrackErrorInMeters == null) { |
|
| 128 | - if (other.averageAbsoluteCrossTrackErrorInMeters != null) |
|
| 137 | + if (currentOrAverageAbsoluteCrossTrackErrorInMeters == null) { |
|
| 138 | + if (other.currentOrAverageAbsoluteCrossTrackErrorInMeters != null) |
|
| 129 | 139 | return false; |
| 130 | - } else if (!averageAbsoluteCrossTrackErrorInMeters.equals(other.averageAbsoluteCrossTrackErrorInMeters)) |
|
| 140 | + } else if (!currentOrAverageAbsoluteCrossTrackErrorInMeters.equals(other.currentOrAverageAbsoluteCrossTrackErrorInMeters)) |
|
| 131 | 141 | return false; |
| 132 | 142 | if (averageManeuverLossInMeters == null) { |
| 133 | 143 | if (other.averageManeuverLossInMeters != null) |
| 134 | 144 | return false; |
| 135 | 145 | } else if (!averageManeuverLossInMeters.equals(other.averageManeuverLossInMeters)) |
| 136 | 146 | return false; |
| 137 | - if (averageSignedCrossTrackErrorInMeters == null) { |
|
| 138 | - if (other.averageSignedCrossTrackErrorInMeters != null) |
|
| 147 | + if (currentOrAverageSignedCrossTrackErrorInMeters == null) { |
|
| 148 | + if (other.currentOrAverageSignedCrossTrackErrorInMeters != null) |
|
| 139 | 149 | return false; |
| 140 | - } else if (!averageSignedCrossTrackErrorInMeters.equals(other.averageSignedCrossTrackErrorInMeters)) |
|
| 150 | + } else if (!currentOrAverageSignedCrossTrackErrorInMeters.equals(other.currentOrAverageSignedCrossTrackErrorInMeters)) |
|
| 141 | 151 | return false; |
| 142 | 152 | if (averageSpeedOverGroundInKnots == null) { |
| 143 | 153 | if (other.averageSpeedOverGroundInKnots != null) |
java/com.sap.sailing.domain.igtimiadapter.gateway/META-INF/MANIFEST.MF
| ... | ... | @@ -25,7 +25,8 @@ Require-Bundle: org.json.simple;bundle-version="1.1.0", |
| 25 | 25 | com.sap.sse.security.common, |
| 26 | 26 | com.sap.sse.security.interface, |
| 27 | 27 | org.apache.shiro.core, |
| 28 | - org.apache.shiro.web |
|
| 28 | + org.apache.shiro.web, |
|
| 29 | + com.sap.sse.replication |
|
| 29 | 30 | Import-Package: com.sap.sse.security.shared, |
| 30 | 31 | javax.ws.rs;version="1.1.1", |
| 31 | 32 | javax.ws.rs.core;version="1.1.1", |
java/com.sap.sailing.domain.igtimiadapter.gateway/src/com/sap/sailing/domain/igtimiadapter/gateway/impl/Activator.java
| 32 | 33 | index 16cf9cf..d677e21 |
| 33 | --- a/java/com.sap.sailing.domain.igtimiadapter.gateway/src/com/sap/sailing/domain/igtimiadapter/gateway/impl/Activator.java |
|
| 34 | +++ b/java/com.sap.sailing.domain.igtimiadapter.gateway/src/com/sap/sailing/domain/igtimiadapter/gateway/impl/Activator.java |
|
| ... | ... | @@ -10,6 +10,8 @@ import org.osgi.util.tracker.ServiceTracker; |
| 10 | 10 | |
| 11 | 11 | import com.sap.sailing.domain.igtimiadapter.Client; |
| 12 | 12 | import com.sap.sailing.domain.igtimiadapter.IgtimiConnectionFactory; |
| 13 | +import com.sap.sse.replication.FullyInitializedReplicableTracker; |
|
| 14 | +import com.sap.sse.replication.ReplicationService; |
|
| 13 | 15 | import com.sap.sse.security.SecurityService; |
| 14 | 16 | import com.sap.sse.util.ServiceTrackerFactory; |
| 15 | 17 | |
| ... | ... | @@ -26,7 +28,7 @@ import com.sap.sse.util.ServiceTrackerFactory; |
| 26 | 28 | public class Activator implements BundleActivator { |
| 27 | 29 | private static Activator INSTANCE; |
| 28 | 30 | private ServiceTracker<IgtimiConnectionFactory, IgtimiConnectionFactory> igtimiConnectionFactoryTracker; |
| 29 | - private ServiceTracker<SecurityService, SecurityService> securityServiceTracker; |
|
| 31 | + private FullyInitializedReplicableTracker<SecurityService> securityServiceTracker; |
|
| 30 | 32 | |
| 31 | 33 | public Activator() throws ClientProtocolException, IllegalStateException, IOException, ParseException { |
| 32 | 34 | } |
| ... | ... | @@ -34,11 +36,11 @@ public class Activator implements BundleActivator { |
| 34 | 36 | @Override |
| 35 | 37 | public void start(final BundleContext context) throws Exception { |
| 36 | 38 | INSTANCE = this; |
| 37 | - |
|
| 38 | 39 | igtimiConnectionFactoryTracker = ServiceTrackerFactory |
| 39 | 40 | .createAndOpen(context, IgtimiConnectionFactory.class); |
| 40 | - |
|
| 41 | - securityServiceTracker = ServiceTrackerFactory.createAndOpen(context, SecurityService.class); |
|
| 41 | + securityServiceTracker = new FullyInitializedReplicableTracker<>(context, SecurityService.class, |
|
| 42 | + /* customizer */ null, ServiceTrackerFactory.createAndOpen(context, ReplicationService.class)); |
|
| 43 | + securityServiceTracker.open(); |
|
| 42 | 44 | } |
| 43 | 45 | |
| 44 | 46 | public static Activator getInstance() throws ClientProtocolException, IllegalStateException, IOException, ParseException { |
| ... | ... | @@ -53,7 +55,11 @@ public class Activator implements BundleActivator { |
| 53 | 55 | } |
| 54 | 56 | |
| 55 | 57 | public SecurityService getSecurityService() { |
| 56 | - return securityServiceTracker.getService(); |
|
| 58 | + try { |
|
| 59 | + return securityServiceTracker.getInitializedService(0); |
|
| 60 | + } catch (InterruptedException e) { |
|
| 61 | + throw new RuntimeException(e); |
|
| 62 | + } |
|
| 57 | 63 | } |
| 58 | 64 | |
| 59 | 65 | @Override |
java/com.sap.sailing.domain.igtimiadapter/src/com/sap/sailing/domain/igtimiadapter/impl/Activator.java
| ... | ... | @@ -13,7 +13,6 @@ import org.apache.http.client.ClientProtocolException; |
| 13 | 13 | import org.json.simple.parser.ParseException; |
| 14 | 14 | import org.osgi.framework.BundleActivator; |
| 15 | 15 | import org.osgi.framework.BundleContext; |
| 16 | -import org.osgi.util.tracker.ServiceTracker; |
|
| 17 | 16 | |
| 18 | 17 | import com.sap.sailing.domain.common.security.SecuredDomainType; |
| 19 | 18 | import com.sap.sailing.domain.igtimiadapter.Account; |
| ... | ... | @@ -23,6 +22,8 @@ import com.sap.sailing.domain.igtimiadapter.persistence.DomainObjectFactory; |
| 23 | 22 | import com.sap.sailing.domain.igtimiadapter.persistence.MongoObjectFactory; |
| 24 | 23 | import com.sap.sailing.domain.igtimiadapter.persistence.PersistenceFactory; |
| 25 | 24 | import com.sap.sailing.domain.tracking.WindTrackerFactory; |
| 25 | +import com.sap.sse.replication.FullyInitializedReplicableTracker; |
|
| 26 | +import com.sap.sse.replication.ReplicationService; |
|
| 26 | 27 | import com.sap.sse.security.SecurityService; |
| 27 | 28 | import com.sap.sse.util.ClearStateTestSupport; |
| 28 | 29 | import com.sap.sse.util.ServiceTrackerFactory; |
| ... | ... | @@ -55,7 +56,7 @@ public class Activator implements BundleActivator { |
| 55 | 56 | private static final String CLIENT_REDIRECT_PORT_PROPERTY_NAME = "igtimi.client.redirect.port"; |
| 56 | 57 | private final Future<IgtimiConnectionFactoryImpl> connectionFactory; |
| 57 | 58 | private final Future<IgtimiWindTrackerFactory> windTrackerFactory; |
| 58 | - private ServiceTracker<SecurityService, SecurityService> securityServiceServiceTracker; |
|
| 59 | + private FullyInitializedReplicableTracker<SecurityService> securityServiceServiceTracker; |
|
| 59 | 60 | private final ExecutorService executor = Executors.newSingleThreadExecutor(new ThreadFactoryWithPriority(Thread.NORM_PRIORITY, /* daemon */ true)); |
| 60 | 61 | private SecurityService securityServiceTest; |
| 61 | 62 | |
| ... | ... | @@ -106,10 +107,12 @@ public class Activator implements BundleActivator { |
| 106 | 107 | } |
| 107 | 108 | } |
| 108 | 109 | }); |
| 109 | - securityServiceServiceTracker = ServiceTrackerFactory.createAndOpen(context, SecurityService.class); |
|
| 110 | + securityServiceServiceTracker = new FullyInitializedReplicableTracker<>(context, SecurityService.class, |
|
| 111 | + /* customizer */ null, ServiceTrackerFactory.createAndOpen(context, ReplicationService.class)); |
|
| 112 | + securityServiceServiceTracker.open(); |
|
| 110 | 113 | new Thread(() -> { |
| 111 | 114 | try { |
| 112 | - final SecurityService securityService = securityServiceServiceTracker.waitForService(0); |
|
| 115 | + final SecurityService securityService = securityServiceServiceTracker.getInitializedService(0); |
|
| 113 | 116 | IgtimiConnectionFactoryImpl igtimiConnectionFactory = connectionFactory.get(); |
| 114 | 117 | for (Account account : igtimiConnectionFactory.getAllAccounts()) { |
| 115 | 118 | securityService.migrateOwnership(account); |
| ... | ... | @@ -144,7 +147,11 @@ public class Activator implements BundleActivator { |
| 144 | 147 | } |
| 145 | 148 | |
| 146 | 149 | public SecurityService getSecurityService() { |
| 147 | - return securityServiceTest == null ? securityServiceServiceTracker.getService() : securityServiceTest; |
|
| 150 | + try { |
|
| 151 | + return securityServiceTest == null ? securityServiceServiceTracker.getInitializedService(0) : securityServiceTest; |
|
| 152 | + } catch (InterruptedException e) { |
|
| 153 | + throw new RuntimeException(e); |
|
| 154 | + } |
|
| 148 | 155 | } |
| 149 | 156 | |
| 150 | 157 | public IgtimiWindTrackerFactory getWindTrackerFactory() { |
java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/Activator.java
| ... | ... | @@ -36,14 +36,16 @@ import com.sap.sailing.server.interfaces.RacingEventService; |
| 36 | 36 | import com.sap.sailing.shared.persistence.device.DeviceIdentifierMongoHandler; |
| 37 | 37 | import com.sap.sse.MasterDataImportClassLoaderService; |
| 38 | 38 | import com.sap.sse.common.TypeBasedServiceFinder; |
| 39 | +import com.sap.sse.replication.FullyInitializedReplicableTracker; |
|
| 39 | 40 | import com.sap.sse.replication.Replicable; |
| 41 | +import com.sap.sse.replication.ReplicationService; |
|
| 40 | 42 | import com.sap.sse.util.ServiceTrackerFactory; |
| 41 | 43 | |
| 42 | 44 | public class Activator implements BundleActivator { |
| 43 | 45 | private static final Logger logger = Logger.getLogger(Activator.class.getName()); |
| 44 | 46 | |
| 45 | 47 | private static BundleContext context; |
| 46 | - private ServiceTracker<RacingEventService, RacingEventService> racingEventServiceTracker; |
|
| 48 | + private FullyInitializedReplicableTracker<RacingEventService> racingEventServiceTracker; |
|
| 47 | 49 | private ServiceTracker<SensorFixMapper<?, ?, ?>, SensorFixMapper<?, ?, ?>> sensorFixMapperTracker; |
| 48 | 50 | |
| 49 | 51 | public static BundleContext getContext() { |
| ... | ... | @@ -84,13 +86,18 @@ public class Activator implements BundleActivator { |
| 84 | 86 | registrations.add(context.registerService(SensorFixMapper.class, new BravoExtendedDataFixMapper(), null)); |
| 85 | 87 | registrations.add(context.registerService(SensorFixMapper.class, new ExpeditionExtendedDataFixMapper(), null)); |
| 86 | 88 | sensorFixMapperTracker = createSensorFixMapperServiceTracker(context); |
| 87 | - racingEventServiceTracker = ServiceTrackerFactory.createAndOpen(context, RacingEventService.class); |
|
| 89 | + racingEventServiceTracker = new FullyInitializedReplicableTracker<>(context, RacingEventService.class, |
|
| 90 | + /* customizer */ null, ServiceTrackerFactory.createAndOpen(context, ReplicationService.class)); |
|
| 91 | + racingEventServiceTracker.open(); |
|
| 88 | 92 | RegattaLogFixTrackerRegattaListener regattaLogSensorDataTrackerTrackedRegattaListener = new RegattaLogFixTrackerRegattaListener( |
| 89 | 93 | racingEventServiceTracker, new SensorFixMapperFactoryImpl(sensorFixMapperTracker)); |
| 90 | 94 | registrations.add(context.registerService(TrackedRegattaListener.class, |
| 91 | 95 | regattaLogSensorDataTrackerTrackedRegattaListener, null)); |
| 96 | + final Dictionary<String, String> replicableServiceProperties = new Hashtable<>(); |
|
| 97 | + replicableServiceProperties.put(Replicable.OSGi_Service_Registry_ID_Property_Name, |
|
| 98 | + regattaLogSensorDataTrackerTrackedRegattaListener.getId().toString()); |
|
| 92 | 99 | registrations.add(context.registerService(Replicable.class, |
| 93 | - regattaLogSensorDataTrackerTrackedRegattaListener, null)); |
|
| 100 | + regattaLogSensorDataTrackerTrackedRegattaListener, replicableServiceProperties)); |
|
| 94 | 101 | new Thread(()->{ |
| 95 | 102 | try { |
| 96 | 103 | registrations.add(context.registerService(RaceTrackingConnectivityParametersHandler.class, |
java/com.sap.sailing.domain.racelogtrackingadapter/src/com/sap/sailing/domain/racelogtracking/impl/fixtracker/RegattaLogFixTrackerRegattaListener.java
| ... | ... | @@ -12,8 +12,6 @@ import java.util.Set; |
| 12 | 12 | import java.util.concurrent.ConcurrentHashMap; |
| 13 | 13 | import java.util.logging.Logger; |
| 14 | 14 | |
| 15 | -import org.osgi.util.tracker.ServiceTracker; |
|
| 16 | - |
|
| 17 | 15 | import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; |
| 18 | 16 | import com.sap.sailing.domain.racelog.tracking.SensorFixStore; |
| 19 | 17 | import com.sap.sailing.domain.racelogsensortracking.SensorFixMapperFactory; |
| ... | ... | @@ -24,14 +22,15 @@ import com.sap.sailing.domain.tracking.RaceTracker; |
| 24 | 22 | import com.sap.sailing.domain.tracking.TrackedRegatta; |
| 25 | 23 | import com.sap.sailing.domain.tracking.TrackedRegattaListener; |
| 26 | 24 | import com.sap.sailing.server.interfaces.RacingEventService; |
| 25 | +import com.sap.sse.replication.FullyInitializedReplicableTracker; |
|
| 27 | 26 | import com.sap.sse.replication.OperationExecutionListener; |
| 28 | 27 | import com.sap.sse.replication.OperationWithResult; |
| 29 | 28 | import com.sap.sse.replication.OperationWithResultWithIdWrapper; |
| 30 | 29 | import com.sap.sse.replication.OperationsToMasterSender; |
| 30 | +import com.sap.sse.replication.OperationsToMasterSendingQueue; |
|
| 31 | 31 | import com.sap.sse.replication.ReplicableWithObjectInputStream; |
| 32 | 32 | import com.sap.sse.replication.ReplicationMasterDescriptor; |
| 33 | 33 | import com.sap.sse.replication.ReplicationService; |
| 34 | -import com.sap.sse.replication.OperationsToMasterSendingQueue; |
|
| 35 | 34 | |
| 36 | 35 | /** |
| 37 | 36 | * This is the main entry point of the {@link SensorFixStore} based fix tracking. |
| ... | ... | @@ -51,7 +50,7 @@ public class RegattaLogFixTrackerRegattaListener extends AbstractTrackedRegattaA |
| 51 | 50 | private static final Logger log = Logger.getLogger(RegattaLogFixTrackerRegattaListener.class.getName()); |
| 52 | 51 | |
| 53 | 52 | private final Map<RegattaAndRaceIdentifier, RaceLogFixTrackerManager> dataTrackers = new ConcurrentHashMap<>(); |
| 54 | - private final ServiceTracker<RacingEventService, RacingEventService> racingEventServiceTracker; |
|
| 53 | + private final FullyInitializedReplicableTracker<RacingEventService> racingEventServiceTracker; |
|
| 55 | 54 | private final SensorFixMapperFactory sensorFixMapperFactory; |
| 56 | 55 | |
| 57 | 56 | /** |
| ... | ... | @@ -63,10 +62,11 @@ public class RegattaLogFixTrackerRegattaListener extends AbstractTrackedRegattaA |
| 63 | 62 | private OperationsToMasterSendingQueue unsentOperationsToMasterSender; |
| 64 | 63 | |
| 65 | 64 | public RegattaLogFixTrackerRegattaListener( |
| 66 | - ServiceTracker<RacingEventService, RacingEventService> racingEventServiceTracker, |
|
| 65 | + FullyInitializedReplicableTracker<RacingEventService> racingEventServiceTracker, |
|
| 67 | 66 | SensorFixMapperFactory sensorFixMapperFactory) { |
| 68 | 67 | this.racingEventServiceTracker = racingEventServiceTracker; |
| 69 | 68 | this.sensorFixMapperFactory = sensorFixMapperFactory; |
| 69 | + this.currentlyFillingFromInitialLoad = false; |
|
| 70 | 70 | } |
| 71 | 71 | |
| 72 | 72 | @Override |
| ... | ... | @@ -81,30 +81,38 @@ public class RegattaLogFixTrackerRegattaListener extends AbstractTrackedRegattaA |
| 81 | 81 | @Override |
| 82 | 82 | protected void onRaceAdded(RegattaAndRaceIdentifier raceIdentifier, DynamicTrackedRegatta trackedRegatta, |
| 83 | 83 | DynamicTrackedRace trackedRace) { |
| 84 | - racingEventServiceTracker.getService().getRaceTrackerByRegattaAndRaceIdentifier(raceIdentifier, (raceTracker) -> { |
|
| 85 | - if (raceTracker != null) { |
|
| 86 | - boolean added = raceTracker.add(new RaceTracker.Listener() { |
|
| 87 | - @Override |
|
| 88 | - public void onTrackerWillStop(boolean preemptive, boolean willBeRemoved) { |
|
| 89 | - raceTracker.remove(this); |
|
| 90 | - removeRaceLogSensorDataTracker(raceIdentifier, preemptive, willBeRemoved); |
|
| 84 | + try { |
|
| 85 | + racingEventServiceTracker.getInitializedService(0).getRaceTrackerByRegattaAndRaceIdentifier(raceIdentifier, (raceTracker) -> { |
|
| 86 | + try { |
|
| 87 | + if (raceTracker != null) { |
|
| 88 | + boolean added = raceTracker.add(new RaceTracker.Listener() { |
|
| 89 | + @Override |
|
| 90 | + public void onTrackerWillStop(boolean preemptive, boolean willBeRemoved) { |
|
| 91 | + raceTracker.remove(this); |
|
| 92 | + removeRaceLogSensorDataTracker(raceIdentifier, preemptive, willBeRemoved); |
|
| 93 | + } |
|
| 94 | + }); |
|
| 95 | + // if !added, the RaceTracker is already stopped, so we are not allowed to start fix tracking |
|
| 96 | + if (added) { |
|
| 97 | + RaceLogFixTrackerManager trackerManager = new RaceLogFixTrackerManager( |
|
| 98 | + (DynamicTrackedRace) trackedRace, racingEventServiceTracker.getInitializedService(0).getSensorFixStore(), |
|
| 99 | + sensorFixMapperFactory); |
|
| 100 | + RaceLogFixTrackerManager oldInstance = null; |
|
| 101 | + synchronized (this) { |
|
| 102 | + oldInstance = dataTrackers.put(raceIdentifier, trackerManager); |
|
| 103 | + } |
|
| 104 | + if (oldInstance != null) { |
|
| 105 | + oldInstance.stop(/* preemptive */ true, /* willBeRemoved */ false); |
|
| 106 | + } |
|
| 107 | + } |
|
| 108 | + } |
|
| 109 | + } catch (InterruptedException e) { |
|
| 110 | + throw new RuntimeException(e); |
|
| 91 | 111 | } |
| 92 | 112 | }); |
| 93 | - // if !added, the RaceTracker is already stopped, so we are not allowed to start fix tracking |
|
| 94 | - if (added) { |
|
| 95 | - RaceLogFixTrackerManager trackerManager = new RaceLogFixTrackerManager( |
|
| 96 | - (DynamicTrackedRace) trackedRace, racingEventServiceTracker.getService().getSensorFixStore(), |
|
| 97 | - sensorFixMapperFactory); |
|
| 98 | - RaceLogFixTrackerManager oldInstance = null; |
|
| 99 | - synchronized (this) { |
|
| 100 | - oldInstance = dataTrackers.put(raceIdentifier, trackerManager); |
|
| 101 | - } |
|
| 102 | - if (oldInstance != null) { |
|
| 103 | - oldInstance.stop(/* preemptive */ true, /* willBeRemoved */ false); |
|
| 104 | - } |
|
| 105 | - } |
|
| 113 | + } catch (InterruptedException e) { |
|
| 114 | + throw new RuntimeException(e); |
|
| 106 | 115 | } |
| 107 | - }); |
|
| 108 | 116 | } |
| 109 | 117 | |
| 110 | 118 | @Override |
| ... | ... | @@ -130,7 +138,7 @@ public class RegattaLogFixTrackerRegattaListener extends AbstractTrackedRegattaA |
| 130 | 138 | |
| 131 | 139 | // Replication related methods and fields |
| 132 | 140 | private final ConcurrentHashMap<OperationExecutionListener<RegattaLogFixTrackerRegattaListener>, OperationExecutionListener<RegattaLogFixTrackerRegattaListener>> operationExecutionListeners = new ConcurrentHashMap<>(); |
| 133 | - private ThreadLocal<Boolean> currentlyFillingFromInitialLoad = ThreadLocal.withInitial(() -> false); |
|
| 141 | + private volatile boolean currentlyFillingFromInitialLoad; |
|
| 134 | 142 | private ThreadLocal<Boolean> currentlyApplyingOperationReceivedFromMaster = ThreadLocal.withInitial(() -> false); |
| 135 | 143 | private final Set<OperationWithResultWithIdWrapper<RegattaLogFixTrackerRegattaListener, ?>> operationsSentToMasterForReplication = new HashSet<>(); |
| 136 | 144 | private ReplicationMasterDescriptor master; |
| ... | ... | @@ -174,12 +182,12 @@ public class RegattaLogFixTrackerRegattaListener extends AbstractTrackedRegattaA |
| 174 | 182 | |
| 175 | 183 | @Override |
| 176 | 184 | public boolean isCurrentlyFillingFromInitialLoad() { |
| 177 | - return currentlyFillingFromInitialLoad.get(); |
|
| 185 | + return currentlyFillingFromInitialLoad; |
|
| 178 | 186 | } |
| 179 | 187 | |
| 180 | 188 | @Override |
| 181 | 189 | public void setCurrentlyFillingFromInitialLoad(boolean currentlyFillingFromInitialLoad) { |
| 182 | - this.currentlyFillingFromInitialLoad.set(currentlyFillingFromInitialLoad); |
|
| 190 | + this.currentlyFillingFromInitialLoad = currentlyFillingFromInitialLoad; |
|
| 183 | 191 | } |
| 184 | 192 | |
| 185 | 193 | @Override |
java/com.sap.sailing.domain.swisstimingadapter.persistence/META-INF/MANIFEST.MF
| ... | ... | @@ -14,7 +14,8 @@ Require-Bundle: com.sap.sailing.domain.swisstimingadapter, |
| 14 | 14 | com.sap.sailing.domain.persistence, |
| 15 | 15 | com.sap.sse, |
| 16 | 16 | org.mongodb.mongo-java-driver;bundle-version="3.6.4", |
| 17 | - com.sap.sse.security.common |
|
| 17 | + com.sap.sse.security.common, |
|
| 18 | + com.sap.sse.replication.interfaces |
|
| 18 | 19 | Export-Package: com.sap.sailing.domain.swisstimingadapter.persistence, |
| 19 | 20 | com.sap.sailing.domain.swisstimingadapter.persistence.impl;x-friends:="com.sap.sailing.domain.swisstimingadapter.test,com.sap.sailing.mongodb.test" |
| 20 | 21 | Import-Package: com.sap.sse.security, |
java/com.sap.sailing.domain.swisstimingadapter.persistence/src/com/sap/sailing/domain/swisstimingadapter/persistence/impl/Activator.java
| ... | ... | @@ -22,6 +22,8 @@ import com.sap.sailing.domain.swisstimingadapter.persistence.SwissTimingAdapterP |
| 22 | 22 | import com.sap.sailing.domain.tracking.RaceTrackingConnectivityParametersHandler; |
| 23 | 23 | import com.sap.sse.common.TypeBasedServiceFinder; |
| 24 | 24 | import com.sap.sse.mongodb.MongoDBService; |
| 25 | +import com.sap.sse.replication.FullyInitializedReplicableTracker; |
|
| 26 | +import com.sap.sse.replication.ReplicationService; |
|
| 25 | 27 | import com.sap.sse.security.SecurityService; |
| 26 | 28 | import com.sap.sse.util.ServiceTrackerFactory; |
| 27 | 29 | |
| ... | ... | @@ -34,8 +36,9 @@ public class Activator implements BundleActivator { |
| 34 | 36 | MongoDBService.INSTANCE.registerExclusively(CollectionNames.class, name.name()); |
| 35 | 37 | } |
| 36 | 38 | new Thread(() -> { |
| 37 | - final ServiceTracker<SecurityService, SecurityService> securityServiceServiceTracker = ServiceTrackerFactory |
|
| 38 | - .createAndOpen(context, SecurityService.class); |
|
| 39 | + final FullyInitializedReplicableTracker<SecurityService> securityServiceServiceTracker = new FullyInitializedReplicableTracker<>(context, SecurityService.class, |
|
| 40 | + /* customizer */ null, ServiceTrackerFactory.createAndOpen(context, ReplicationService.class)); |
|
| 41 | + securityServiceServiceTracker.open(); |
|
| 39 | 42 | final ServiceTracker<MongoObjectFactory, MongoObjectFactory> mongoObjectFactoryServiceTracker = ServiceTrackerFactory.createAndOpen(context, MongoObjectFactory.class); |
| 40 | 43 | final ServiceTracker<DomainObjectFactory, DomainObjectFactory> domainObjectFactoryServiceTracker = ServiceTrackerFactory.createAndOpen(context, DomainObjectFactory.class); |
| 41 | 44 | final ServiceTracker<SwissTimingAdapterFactory, SwissTimingAdapterFactory> swissTimingAdapterFactoryServiceTracker = ServiceTrackerFactory.createAndOpen(context, SwissTimingAdapterFactory.class); |
| ... | ... | @@ -43,7 +46,7 @@ public class Activator implements BundleActivator { |
| 43 | 46 | final MongoObjectFactory mongoObjectFactory = mongoObjectFactoryServiceTracker.waitForService(0); |
| 44 | 47 | final DomainObjectFactory domainObjectFactory = domainObjectFactoryServiceTracker.waitForService(0); |
| 45 | 48 | final SwissTimingAdapterFactory swissTimingAdapterFactory = swissTimingAdapterFactoryServiceTracker.waitForService(0); |
| 46 | - final SecurityService securityService = securityServiceServiceTracker.waitForService(0); |
|
| 49 | + final SecurityService securityService = securityServiceServiceTracker.getInitializedService(0); |
|
| 47 | 50 | final Dictionary<String, Object> properties = new Hashtable<String, Object>(); |
| 48 | 51 | final com.sap.sailing.domain.swisstimingadapter.DomainFactory domainFactory = swissTimingAdapterFactory.getOrCreateSwissTimingAdapter( |
| 49 | 52 | domainObjectFactory.getBaseDomainFactory()).getSwissTimingDomainFactory(); |
| ... | ... | @@ -53,21 +56,17 @@ public class Activator implements BundleActivator { |
| 53 | 56 | domainFactory); |
| 54 | 57 | properties.put(TypeBasedServiceFinder.TYPE, SwissTimingTrackingConnectivityParameters.TYPE); |
| 55 | 58 | context.registerService(RaceTrackingConnectivityParametersHandler.class, paramsHandler, properties); |
| 56 | - |
|
| 57 | 59 | for (SwissTimingArchiveConfiguration swissTimingArchive : SwissTimingAdapterPersistence.INSTANCE |
| 58 | 60 | .getSwissTimingArchiveConfigurations()) { |
| 59 | 61 | securityService.migrateOwnership(swissTimingArchive); |
| 60 | 62 | } |
| 61 | - |
|
| 62 | 63 | for (SwissTimingConfiguration swissTiming : SwissTimingAdapterPersistence.INSTANCE |
| 63 | 64 | .getSwissTimingConfigurations()) { |
| 64 | 65 | securityService.migrateOwnership(swissTiming); |
| 65 | 66 | } |
| 66 | - |
|
| 67 | 67 | // we do not necessarily have swisstiming configs, so ensure that migration is marked as done |
| 68 | 68 | securityService.assumeOwnershipMigrated(SecuredDomainType.SWISS_TIMING_ACCOUNT.getName()); |
| 69 | 69 | securityService.assumeOwnershipMigrated(SecuredDomainType.SWISS_TIMING_ARCHIVE_ACCOUNT.getName()); |
| 70 | - |
|
| 71 | 70 | } catch (Exception e) { |
| 72 | 71 | logger.log(Level.SEVERE, "Exception trying to register SwissTiming RaceTrackingConnectivityParametersHandler implementation", e); |
| 73 | 72 | } |
java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/TestDeadlockInRegattaListener.java
| ... | ... | @@ -14,7 +14,6 @@ import java.util.function.Consumer; |
| 14 | 14 | import org.junit.Rule; |
| 15 | 15 | import org.junit.Test; |
| 16 | 16 | import org.junit.rules.Timeout; |
| 17 | -import org.osgi.util.tracker.ServiceTracker; |
|
| 18 | 17 | |
| 19 | 18 | import com.sap.sailing.domain.base.Course; |
| 20 | 19 | import com.sap.sailing.domain.base.RaceDefinition; |
| ... | ... | @@ -35,6 +34,7 @@ import com.sap.sailing.domain.tracking.impl.DynamicTrackedRaceImpl; |
| 35 | 34 | import com.sap.sailing.domain.tracking.impl.DynamicTrackedRegattaImpl; |
| 36 | 35 | import com.sap.sailing.server.impl.RacingEventServiceImpl; |
| 37 | 36 | import com.sap.sailing.server.interfaces.RacingEventService; |
| 37 | +import com.sap.sse.replication.FullyInitializedReplicableTracker; |
|
| 38 | 38 | import com.sap.sse.util.ThreadLocalTransporter; |
| 39 | 39 | |
| 40 | 40 | public class TestDeadlockInRegattaListener { |
| ... | ... | @@ -46,8 +46,8 @@ public class TestDeadlockInRegattaListener { |
| 46 | 46 | CyclicBarrier latch = new CyclicBarrier(2); |
| 47 | 47 | CyclicBarrier monitorOnRegattaListenerLatch = new CyclicBarrier(2); |
| 48 | 48 | @SuppressWarnings("unchecked") |
| 49 | - ServiceTracker<RacingEventService, RacingEventService> racingEventServiceTracker = |
|
| 50 | - (ServiceTracker<RacingEventService, RacingEventService>) mock(ServiceTracker.class); |
|
| 49 | + FullyInitializedReplicableTracker<RacingEventService> racingEventServiceTracker = |
|
| 50 | + (FullyInitializedReplicableTracker<RacingEventService>) mock(FullyInitializedReplicableTracker.class); |
|
| 51 | 51 | final String regattaName = "Test Regatta"; |
| 52 | 52 | final RegattaImpl regatta = new RegattaImpl( |
| 53 | 53 | /* raceLogStore */ null, EmptyRegattaLogStore.INSTANCE, regattaName, |
| ... | ... | @@ -102,7 +102,7 @@ public class TestDeadlockInRegattaListener { |
| 102 | 102 | } |
| 103 | 103 | } |
| 104 | 104 | }; |
| 105 | - when(racingEventServiceTracker.getService()).thenReturn(racingEventService); |
|
| 105 | + when(racingEventServiceTracker.getInitializedService(0)).thenReturn(racingEventService); |
|
| 106 | 106 | RegattaLogFixTrackerRegattaListener listener = new RegattaLogFixTrackerRegattaListener( |
| 107 | 107 | racingEventServiceTracker, null); |
| 108 | 108 |
java/com.sap.sailing.domain.tractracadapter.persistence/META-INF/MANIFEST.MF
| ... | ... | @@ -14,7 +14,8 @@ Require-Bundle: com.sap.sse.mongodb, |
| 14 | 14 | com.sap.sailing.domain.persistence, |
| 15 | 15 | com.sap.sse, |
| 16 | 16 | org.mongodb.mongo-java-driver;bundle-version="3.6.4", |
| 17 | - com.sap.sse.security.common |
|
| 17 | + com.sap.sse.security.common, |
|
| 18 | + com.sap.sse.replication.interfaces |
|
| 18 | 19 | Export-Package: com.sap.sailing.domain.tractracadapter.persistence, |
| 19 | 20 | com.sap.sailing.domain.tractracadapter.persistence.impl;x-friends:="com.sap.sailing.mongodb.test" |
| 20 | 21 | Import-Package: com.sap.sse.security, |
java/com.sap.sailing.domain.tractracadapter.persistence/src/com/sap/sailing/domain/tractracadapter/persistence/impl/Activator.java
| ... | ... | @@ -23,18 +23,20 @@ import com.sap.sailing.domain.tractracadapter.TracTracConfiguration; |
| 23 | 23 | import com.sap.sailing.domain.tractracadapter.impl.RaceTrackingConnectivityParametersImpl; |
| 24 | 24 | import com.sap.sse.common.TypeBasedServiceFinder; |
| 25 | 25 | import com.sap.sse.mongodb.MongoDBService; |
| 26 | +import com.sap.sse.replication.FullyInitializedReplicableTracker; |
|
| 27 | +import com.sap.sse.replication.ReplicationService; |
|
| 26 | 28 | import com.sap.sse.security.SecurityService; |
| 27 | 29 | import com.sap.sse.util.ClearStateTestSupport; |
| 28 | 30 | import com.sap.sse.util.ServiceTrackerFactory; |
| 29 | 31 | |
| 30 | 32 | public class Activator implements BundleActivator { |
| 31 | 33 | private static final Logger logger = Logger.getLogger(Activator.class.getName()); |
| 32 | - |
|
| 34 | + |
|
| 33 | 35 | /** |
| 34 | 36 | * Requires a base {@link DomainFactory} as well as a {@link RaceLogStore} and a {@link RegattaLogStore}. In order |
| 35 | - * to instantiate those two stores, the method requires a {@link MongoObjectFactory} and a {@link DomainObjectFactory} |
|
| 36 | - * which are waited for as services from the OSGi registry. This happens in a separate thread that the {@link #start(BundleContext)} |
|
| 37 | - * method launches. |
|
| 37 | + * to instantiate those two stores, the method requires a {@link MongoObjectFactory} and a |
|
| 38 | + * {@link DomainObjectFactory} which are waited for as services from the OSGi registry. This happens in a separate |
|
| 39 | + * thread that the {@link #start(BundleContext)} method launches. |
|
| 38 | 40 | */ |
| 39 | 41 | @Override |
| 40 | 42 | public void start(BundleContext context) throws Exception { |
| ... | ... | @@ -42,22 +44,29 @@ public class Activator implements BundleActivator { |
| 42 | 44 | MongoDBService.INSTANCE.registerExclusively(CollectionNames.class, name.name()); |
| 43 | 45 | } |
| 44 | 46 | new Thread(() -> { |
| 45 | - final ServiceTracker<MongoObjectFactory, MongoObjectFactory> mongoObjectFactoryServiceTracker = ServiceTrackerFactory.createAndOpen(context, MongoObjectFactory.class); |
|
| 46 | - final ServiceTracker<DomainObjectFactory, DomainObjectFactory> domainObjectFactoryServiceTracker = ServiceTrackerFactory.createAndOpen(context, DomainObjectFactory.class); |
|
| 47 | - final ServiceTracker<SecurityService, SecurityService> securityServiceServiceTracker = ServiceTrackerFactory |
|
| 48 | - .createAndOpen(context, SecurityService.class); |
|
| 49 | - final ServiceTracker<TracTracAdapterFactory, TracTracAdapterFactory> tractracAdapterFactoryTracker = ServiceTrackerFactory.createAndOpen(context, TracTracAdapterFactory.class); |
|
| 47 | + final ServiceTracker<MongoObjectFactory, MongoObjectFactory> mongoObjectFactoryServiceTracker = ServiceTrackerFactory |
|
| 48 | + .createAndOpen(context, MongoObjectFactory.class); |
|
| 49 | + final ServiceTracker<DomainObjectFactory, DomainObjectFactory> domainObjectFactoryServiceTracker = ServiceTrackerFactory |
|
| 50 | + .createAndOpen(context, DomainObjectFactory.class); |
|
| 51 | + final FullyInitializedReplicableTracker<SecurityService> securityServiceServiceTracker = new FullyInitializedReplicableTracker<>( |
|
| 52 | + context, SecurityService.class, /* customizer */ null, |
|
| 53 | + ServiceTrackerFactory.createAndOpen(context, ReplicationService.class)); |
|
| 54 | + securityServiceServiceTracker.open(); |
|
| 55 | + final ServiceTracker<TracTracAdapterFactory, TracTracAdapterFactory> tractracAdapterFactoryTracker = ServiceTrackerFactory |
|
| 56 | + .createAndOpen(context, TracTracAdapterFactory.class); |
|
| 50 | 57 | try { |
| 51 | 58 | final MongoObjectFactory mongoObjectFactory = mongoObjectFactoryServiceTracker.waitForService(0); |
| 52 | 59 | final DomainObjectFactory domainObjectFactory = domainObjectFactoryServiceTracker.waitForService(0); |
| 53 | - final SecurityService securityService = securityServiceServiceTracker.waitForService(0); |
|
| 60 | + final SecurityService securityService = securityServiceServiceTracker.getInitializedService(0); |
|
| 54 | 61 | final TracTracAdapterFactory tractracAdapterFactory = tractracAdapterFactoryTracker.waitForService(0); |
| 55 | 62 | final Dictionary<String, Object> properties = new Hashtable<String, Object>(); |
| 56 | - final com.sap.sailing.domain.tractracadapter.DomainFactory domainFactory = tractracAdapterFactory.getOrCreateTracTracAdapter( |
|
| 57 | - domainObjectFactory.getBaseDomainFactory()).getTracTracDomainFactory(); |
|
| 63 | + final com.sap.sailing.domain.tractracadapter.DomainFactory domainFactory = tractracAdapterFactory |
|
| 64 | + .getOrCreateTracTracAdapter(domainObjectFactory.getBaseDomainFactory()) |
|
| 65 | + .getTracTracDomainFactory(); |
|
| 58 | 66 | final TracTracConnectivityParamsHandler paramsHandler = new TracTracConnectivityParamsHandler( |
| 59 | 67 | MongoRaceLogStoreFactory.INSTANCE.getMongoRaceLogStore(mongoObjectFactory, domainObjectFactory), |
| 60 | - MongoRegattaLogStoreFactory.INSTANCE.getMongoRegattaLogStore(mongoObjectFactory, domainObjectFactory), |
|
| 68 | + MongoRegattaLogStoreFactory.INSTANCE.getMongoRegattaLogStore(mongoObjectFactory, |
|
| 69 | + domainObjectFactory), |
|
| 61 | 70 | domainFactory); |
| 62 | 71 | |
| 63 | 72 | com.sap.sailing.domain.tractracadapter.persistence.DomainObjectFactory tractracDomainObjectFactory = com.sap.sailing.domain.tractracadapter.persistence.PersistenceFactory.INSTANCE |
| ... | ... | @@ -79,7 +88,9 @@ public class Activator implements BundleActivator { |
| 79 | 88 | } |
| 80 | 89 | }, null); |
| 81 | 90 | } catch (Exception e) { |
| 82 | - logger.log(Level.SEVERE, "Exception trying to register TracTrac RaceTrackingConnectivityParametersHandler implementation", e); |
|
| 91 | + logger.log(Level.SEVERE, |
|
| 92 | + "Exception trying to register TracTrac RaceTrackingConnectivityParametersHandler implementation", |
|
| 93 | + e); |
|
| 83 | 94 | } |
| 84 | 95 | }, getClass().getName() + " registering connectivity handler").start(); |
| 85 | 96 | } |
java/com.sap.sailing.domain/META-INF/MANIFEST.MF
| ... | ... | @@ -30,7 +30,6 @@ Export-Package: com.sap.sailing.domain.base, |
| 30 | 30 | com.sap.sailing.domain.regattalog, |
| 31 | 31 | com.sap.sailing.domain.regattalog.impl, |
| 32 | 32 | com.sap.sailing.domain.sharding, |
| 33 | - com.sap.sailing.domain.sharedsailingdata, |
|
| 34 | 33 | com.sap.sailing.domain.trackfiles, |
| 35 | 34 | com.sap.sailing.domain.trackimport, |
| 36 | 35 | com.sap.sailing.domain.tracking, |
java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractLeaderboardWithCache.java
| ... | ... | @@ -986,24 +986,32 @@ public abstract class AbstractLeaderboardWithCache implements Leaderboard { |
| 986 | 986 | } |
| 987 | 987 | final Speed averageSpeedOverGround = trackedLeg.getAverageSpeedOverGround(timePoint); |
| 988 | 988 | result.averageSpeedOverGroundInKnots = averageSpeedOverGround == null ? null : averageSpeedOverGround.getKnots(); |
| 989 | - Distance averageAbsoluteCrossTrackError; |
|
| 989 | + final boolean hasFinishedLeg = trackedLeg.hasFinishedLeg(timePoint); |
|
| 990 | + Distance currentOrAverageAbsoluteCrossTrackError; |
|
| 990 | 991 | try { |
| 991 | - averageAbsoluteCrossTrackError = trackedLeg.getAverageAbsoluteCrossTrackError(timePoint, waitForLatestAnalyses); |
|
| 992 | + if (hasFinishedLeg) { |
|
| 993 | + currentOrAverageAbsoluteCrossTrackError = trackedLeg.getAverageAbsoluteCrossTrackError(timePoint, waitForLatestAnalyses); |
|
| 994 | + } else { |
|
| 995 | + currentOrAverageAbsoluteCrossTrackError = trackedLeg.getAbsoluteCrossTrackError(timePoint); |
|
| 996 | + } |
|
| 992 | 997 | } catch (NoWindException nwe) { |
| 993 | 998 | // leave averageAbsoluteCrossTrackError as null, meaning "unknown" |
| 994 | - averageAbsoluteCrossTrackError = null; |
|
| 999 | + currentOrAverageAbsoluteCrossTrackError = null; |
|
| 995 | 1000 | } |
| 996 | - result.averageAbsoluteCrossTrackErrorInMeters = averageAbsoluteCrossTrackError == null ? null : averageAbsoluteCrossTrackError.getMeters(); |
|
| 997 | - Distance averageSignedCrossTrackError; |
|
| 1001 | + result.currentOrAverageAbsoluteCrossTrackErrorInMeters = currentOrAverageAbsoluteCrossTrackError == null ? null : currentOrAverageAbsoluteCrossTrackError.getMeters(); |
|
| 1002 | + Distance currentOrAverageSignedCrossTrackError; |
|
| 998 | 1003 | try { |
| 999 | - averageSignedCrossTrackError = trackedLeg.getAverageSignedCrossTrackError(timePoint, waitForLatestAnalyses); |
|
| 1004 | + if (hasFinishedLeg) { |
|
| 1005 | + currentOrAverageSignedCrossTrackError = trackedLeg.getAverageSignedCrossTrackError(timePoint, waitForLatestAnalyses); |
|
| 1006 | + } else { |
|
| 1007 | + currentOrAverageSignedCrossTrackError = trackedLeg.getSignedCrossTrackError(timePoint); |
|
| 1008 | + } |
|
| 1000 | 1009 | } catch (NoWindException nwe) { |
| 1001 | 1010 | // leave averageSignedCrossTrackError as null, meaning "unknown" |
| 1002 | - averageSignedCrossTrackError = null; |
|
| 1011 | + currentOrAverageSignedCrossTrackError = null; |
|
| 1003 | 1012 | } |
| 1004 | - result.averageSignedCrossTrackErrorInMeters = averageSignedCrossTrackError == null ? null : averageSignedCrossTrackError.getMeters(); |
|
| 1013 | + result.currentOrAverageSignedCrossTrackErrorInMeters = currentOrAverageSignedCrossTrackError == null ? null : currentOrAverageSignedCrossTrackError.getMeters(); |
|
| 1005 | 1014 | Double speedOverGroundInKnots; |
| 1006 | - final boolean hasFinishedLeg = trackedLeg.hasFinishedLeg(timePoint); |
|
| 1007 | 1015 | if (hasFinishedLeg) { |
| 1008 | 1016 | speedOverGroundInKnots = averageSpeedOverGround == null ? null : averageSpeedOverGround.getKnots(); |
| 1009 | 1017 | final Distance averageRideHeight = trackedLeg.getAverageRideHeight(timePoint); |
| ... | ... | @@ -1039,7 +1047,7 @@ public abstract class AbstractLeaderboardWithCache implements Leaderboard { |
| 1039 | 1047 | // calls. To avoid having to use expensive locking, we'll just double-check here if legFinishTime is null and |
| 1040 | 1048 | // treat this as if trackedLeg.hasFinishedLeg(timePoint) had returned false. |
| 1041 | 1049 | result.correctedTotalTime = trackedLeg.hasStartedLeg(timePoint) ? trackedLeg.getTrackedLeg().getTrackedRace().getRankingMetric().getCorrectedTime(trackedLeg.getCompetitor(), |
| 1042 | - trackedLeg.hasFinishedLeg(timePoint) && legFinishTime != null ? legFinishTime : timePoint, cache) : null; |
|
| 1050 | + hasFinishedLeg && legFinishTime != null ? legFinishTime : timePoint, cache) : null; |
|
| 1043 | 1051 | // fetch the leg gap in own corrected time from the ranking metric |
| 1044 | 1052 | final Duration gapToLeaderInOwnTime = trackedLeg.getTrackedLeg().getTrackedRace().getRankingMetric(). |
| 1045 | 1053 | getLegGapToLegLeaderInOwnTime(trackedLeg, timePoint, rankingInfo, cache); |
| ... | ... | @@ -1058,7 +1066,7 @@ public abstract class AbstractLeaderboardWithCache implements Leaderboard { |
| 1058 | 1066 | } |
| 1059 | 1067 | result.started = trackedLeg.hasStartedLeg(timePoint); |
| 1060 | 1068 | Speed velocityMadeGood; |
| 1061 | - if (trackedLeg.hasFinishedLeg(timePoint)) { |
|
| 1069 | + if (hasFinishedLeg) { |
|
| 1062 | 1070 | velocityMadeGood = trackedLeg.getAverageVelocityMadeGood(timePoint); |
| 1063 | 1071 | } else { |
| 1064 | 1072 | velocityMadeGood = trackedLeg.getVelocityMadeGood(timePoint, WindPositionMode.EXACT, cache); |
java/com.sap.sailing.domain/src/com/sap/sailing/domain/sharedsailingdata/SharedSailingData.java
| ... | ... | @@ -1,140 +0,0 @@ |
| 1 | -package com.sap.sailing.domain.sharedsailingdata; |
|
| 2 | - |
|
| 3 | -import java.net.URL; |
|
| 4 | -import java.util.ArrayList; |
|
| 5 | -import java.util.Map; |
|
| 6 | -import java.util.Optional; |
|
| 7 | -import java.util.UUID; |
|
| 8 | - |
|
| 9 | -import com.sap.sailing.domain.common.DeviceIdentifier; |
|
| 10 | -import com.sap.sailing.domain.common.Position; |
|
| 11 | -import com.sap.sailing.domain.coursetemplate.CommonMarkProperties; |
|
| 12 | -import com.sap.sailing.domain.coursetemplate.CourseTemplate; |
|
| 13 | -import com.sap.sailing.domain.coursetemplate.FixedPositioning; |
|
| 14 | -import com.sap.sailing.domain.coursetemplate.MarkProperties; |
|
| 15 | -import com.sap.sailing.domain.coursetemplate.MarkRole; |
|
| 16 | -import com.sap.sailing.domain.coursetemplate.MarkTemplate; |
|
| 17 | -import com.sap.sailing.domain.coursetemplate.Positioning; |
|
| 18 | -import com.sap.sailing.domain.coursetemplate.RepeatablePart; |
|
| 19 | -import com.sap.sailing.domain.coursetemplate.WaypointTemplate; |
|
| 20 | -import com.sap.sse.common.TimePoint; |
|
| 21 | -import com.sap.sse.common.impl.MillisecondsTimePoint; |
|
| 22 | -import com.sap.sse.security.SecurityService; |
|
| 23 | -import com.sap.sse.security.shared.HasPermissions.DefaultActions; |
|
| 24 | -import com.sap.sse.security.shared.impl.UserGroup; |
|
| 25 | - |
|
| 26 | -/** |
|
| 27 | - * {@link com.sap.sailing.server.interfaces.RacingEventService RacingEventService} is scoped to one server only. To |
|
| 28 | - * share data that is not exclusively bound to one specific server but relevant for several servers, |
|
| 29 | - * {@link SharedSailingData} provides an alternative replication scope. This means {@link SharedSailingData} may be |
|
| 30 | - * replicated in a similar way as {@link SecurityService}.<br> |
|
| 31 | - * {@link SharedSailingData} encapsulates persistence, replication and security aspects for the following domain types: |
|
| 32 | - * <ul> |
|
| 33 | - * <li>{@link MarkTemplate}s</li> |
|
| 34 | - * <li>{@link MarkProperties}</li> |
|
| 35 | - * <li>{@link CourseTemplate}s</li> |
|
| 36 | - * </ul> |
|
| 37 | - * |
|
| 38 | - * In particular, the reading methods that produce iterables of entities, such as {@link #getAllMarkRoles()} or |
|
| 39 | - * {@link #getAllMarkTemplates()}, will deliver a view restricted by the availability of the {@link DefaultActions#READ} |
|
| 40 | - * permission of the user on whose behalf the request is being executed. |
|
| 41 | - * |
|
| 42 | - * @author Axel Uhl (D043530) |
|
| 43 | - * |
|
| 44 | - */ |
|
| 45 | -public interface SharedSailingData { |
|
| 46 | - |
|
| 47 | - Iterable<MarkProperties> getAllMarkProperties(Iterable<String> tagsToFilterFor); |
|
| 48 | - |
|
| 49 | - Iterable<MarkTemplate> getAllMarkTemplates(); |
|
| 50 | - |
|
| 51 | - Iterable<CourseTemplate> getAllCourseTemplates(Iterable<String> tagsToFilterFor); |
|
| 52 | - |
|
| 53 | - Iterable<MarkRole> getAllMarkRoles(); |
|
| 54 | - |
|
| 55 | - MarkRole createMarkRole(String name, String shortName); |
|
| 56 | - |
|
| 57 | - MarkRole getMarkRoleById(UUID id); |
|
| 58 | - |
|
| 59 | - /** |
|
| 60 | - * @param optionalNonDefaultGroupOwnership |
|
| 61 | - * if {@link Optional#isPresent() present}, defines the {@link UserGroup} to use for the new mark |
|
| 62 | - * properties object's group ownership. Otherwise, the session user's default creation group ownership |
|
| 63 | - * will be used for the current server. |
|
| 64 | - */ |
|
| 65 | - MarkProperties createMarkProperties(CommonMarkProperties properties, Iterable<String> tags, Optional<UserGroup> optionalNonDefaultGroupOwnership); |
|
| 66 | - |
|
| 67 | - MarkProperties updateMarkProperties(UUID uuid, CommonMarkProperties properties, Iterable<String> tags); |
|
| 68 | - |
|
| 69 | - /** |
|
| 70 | - * @param positioningInformation |
|
| 71 | - * if {@code null}, no update will be performed to the positioning information of the |
|
| 72 | - * {@link MarkProperties} object identified by {@code uuid}. To clear the positioning information, pass |
|
| 73 | - * in a valid, non-{@code null} {@link Positioning} object that makes an "empty" specification, such as a |
|
| 74 | - * {@link FixedPositioning} with a {@code null} {@link FixedPositioning#getFixedPosition()} return. |
|
| 75 | - */ |
|
| 76 | - MarkProperties updateMarkProperties(UUID uuid, CommonMarkProperties properties, Positioning positioningInformation, Iterable<String> tags); |
|
| 77 | - |
|
| 78 | - /** |
|
| 79 | - * This overrides a previously set fixed position or associated tracking device. |
|
| 80 | - */ |
|
| 81 | - void setFixedPositionForMarkProperties(MarkProperties markProperties, Position position); |
|
| 82 | - |
|
| 83 | - MarkProperties getMarkPropertiesById(UUID id); |
|
| 84 | - |
|
| 85 | - /** |
|
| 86 | - * This overrides a previously set fixed position or associated tracking device. |
|
| 87 | - */ |
|
| 88 | - void setTrackingDeviceIdentifierForMarkProperties(MarkProperties markProperties, DeviceIdentifier deviceIdentifier); |
|
| 89 | - |
|
| 90 | - MarkTemplate createMarkTemplate(CommonMarkProperties properties); |
|
| 91 | - |
|
| 92 | - MarkTemplate getMarkTemplateById(UUID id); |
|
| 93 | - |
|
| 94 | - /** |
|
| 95 | - * @param waypoints the waypoints in their defined order (iteration order equals order of waypoints in course) |
|
| 96 | - */ |
|
| 97 | - CourseTemplate createCourseTemplate(String courseTemplateName, String courseTemplateShortName, |
|
| 98 | - Iterable<MarkTemplate> marks, Iterable<WaypointTemplate> waypoints, |
|
| 99 | - Map<MarkTemplate, MarkRole> associatedRoles, Map<MarkRole, MarkTemplate> defaultMarkTemplatesForMarkRoles, |
|
| 100 | - RepeatablePart optionalRepeatablePart, Iterable<String> tags, URL optionalImageURL, Integer defaultNumberOfLaps); |
|
| 101 | - |
|
| 102 | - CourseTemplate getCourseTemplateById(UUID id); |
|
| 103 | - |
|
| 104 | - /** |
|
| 105 | - * Records the fact that the {@code markProperties} were used to configure a mark based on a |
|
| 106 | - * {@code markTemplate}. Keeps the {@link MillisecondsTimePoint#now() current time} of this call which will be |
|
| 107 | - * returned for {@code markTemplate} when invoking {@link #getUsedMarkProperties(MarkTemplate)}. |
|
| 108 | - */ |
|
| 109 | - void recordUsage(MarkTemplate markTemplate, MarkProperties markProperties); |
|
| 110 | - |
|
| 111 | - /** |
|
| 112 | - * Records the fact that the {@code markProperties} were used to configure a mark that takes the given role name. |
|
| 113 | - * Keeps the {@link MillisecondsTimePoint#now() current time} of this call which will be returned for the role name |
|
| 114 | - * when invoking {@link #getUsedMarkProperties(String)}. |
|
| 115 | - */ |
|
| 116 | - void recordUsage(MarkProperties markProperties, MarkRole markRole); |
|
| 117 | - |
|
| 118 | - /** |
|
| 119 | - * Returns the time points when {@link MarkProperties} objects were {@link #recordUsage(MarkTemplate, MarkProperties) last used} |
|
| 120 | - * for the {@link MarkTemplate} passed in the {@code markTemplate} parameter. |
|
| 121 | - */ |
|
| 122 | - Map<MarkProperties, TimePoint> getUsedMarkProperties(MarkTemplate markTemplate); |
|
| 123 | - |
|
| 124 | - /** |
|
| 125 | - * Returns the time points when {@link MarkProperties} objects were {@link #recordUsage(MarkProperties, String) last used} |
|
| 126 | - * for the role name passed in the {@code roleName} parameter. |
|
| 127 | - */ |
|
| 128 | - Map<MarkProperties, TimePoint> getUsedMarkProperties(MarkRole roleName); |
|
| 129 | - |
|
| 130 | - void deleteMarkProperties(MarkProperties markProperties); |
|
| 131 | - |
|
| 132 | - void deleteCourseTemplate(CourseTemplate courseTemplate); |
|
| 133 | - |
|
| 134 | - Iterable<MarkProperties> getAllMarkProperties(); |
|
| 135 | - |
|
| 136 | - Iterable<CourseTemplate> getAllCourseTemplates(); |
|
| 137 | - |
|
| 138 | - CourseTemplate updateCourseTemplate(UUID uuid, String name, String shortName, URL optionalImageURL, ArrayList<String> tags, |
|
| 139 | - Integer defaultNumberOfLaps); |
|
| 140 | -} |
java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/TrackedLegOfCompetitor.java
| ... | ... | @@ -289,6 +289,14 @@ public interface TrackedLegOfCompetitor extends Serializable { |
| 289 | 289 | * occurred then the time point of the mark passing of the leg end mark will be taken into account. |
| 290 | 290 | */ |
| 291 | 291 | Distance getAverageAbsoluteCrossTrackError(TimePoint timePoint, boolean waitForLatestAnalysis) throws NoWindException; |
| 292 | + |
|
| 293 | + /** |
|
| 294 | + * Computes the current absolute cross-track error (positive sign or zero, regardless of whether the competitor |
|
| 295 | + * is right or left of the course middle line of the current leg). |
|
| 296 | + * |
|
| 297 | + * @return {@code null} if the competitor has not started or already finished the leg |
|
| 298 | + */ |
|
| 299 | + Distance getAbsoluteCrossTrackError(TimePoint timePoint) throws NoWindException; |
|
| 292 | 300 | |
| 293 | 301 | /** |
| 294 | 302 | * Computes the average signed cross track error for this leg. The cross track error for each fix is taken to be a |
| ... | ... | @@ -299,6 +307,14 @@ public interface TrackedLegOfCompetitor extends Serializable { |
| 299 | 307 | */ |
| 300 | 308 | Distance getAverageSignedCrossTrackError(TimePoint timePoint, boolean waitForLatestAnalysis) throws NoWindException; |
| 301 | 309 | |
| 310 | + /** |
|
| 311 | + * Computes the current signed cross-track error (negative sign means left of the course middle line looking in the direction |
|
| 312 | + * of the leg; positive sign means right of the course middle line). |
|
| 313 | + * |
|
| 314 | + * @return {@code null} if the competitor has not started or already finished the leg |
|
| 315 | + */ |
|
| 316 | + Distance getSignedCrossTrackError(TimePoint timePoint) throws NoWindException; |
|
| 317 | + |
|
| 302 | 318 | TrackedLeg getTrackedLeg(); |
| 303 | 319 | |
| 304 | 320 | /** |
java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedLegOfCompetitorImpl.java
| ... | ... | @@ -547,6 +547,16 @@ public class TrackedLegOfCompetitorImpl implements TrackedLegOfCompetitor { |
| 547 | 547 | } |
| 548 | 548 | |
| 549 | 549 | @Override |
| 550 | + public Distance getAbsoluteCrossTrackError(TimePoint timePoint) throws NoWindException { |
|
| 551 | + return getTrackedLeg().getAbsoluteCrossTrackError(getTrackedRace().getTrack(getCompetitor()).getEstimatedPosition(timePoint, /* extrapolate */ true), timePoint); |
|
| 552 | + } |
|
| 553 | + |
|
| 554 | + @Override |
|
| 555 | + public Distance getSignedCrossTrackError(TimePoint timePoint) throws NoWindException { |
|
| 556 | + return getTrackedLeg().getSignedCrossTrackError(getTrackedRace().getTrack(getCompetitor()).getEstimatedPosition(timePoint, /* extrapolate */ true), timePoint); |
|
| 557 | + } |
|
| 558 | + |
|
| 559 | + @Override |
|
| 550 | 560 | public Duration getGapToLeader(TimePoint timePoint, final Competitor leaderInLegAtTimePoint, |
| 551 | 561 | final RankingInfo rankingInfo, WindPositionMode windPositionMode) throws NoWindException { |
| 552 | 562 | return getGapToLeader(timePoint, leaderInLegAtTimePoint, windPositionMode, rankingInfo, new LeaderboardDTOCalculationReuseCache(timePoint)); |
java/com.sap.sailing.expeditionconnector/src/com/sap/sailing/expeditionconnector/impl/Activator.java
| ... | ... | @@ -12,7 +12,6 @@ import java.util.logging.Logger; |
| 12 | 12 | import org.osgi.framework.BundleActivator; |
| 13 | 13 | import org.osgi.framework.BundleContext; |
| 14 | 14 | import org.osgi.framework.ServiceRegistration; |
| 15 | -import org.osgi.util.tracker.ServiceTracker; |
|
| 16 | 15 | |
| 17 | 16 | import com.sap.sailing.domain.common.security.SecuredDomainType; |
| 18 | 17 | import com.sap.sailing.domain.racelogtracking.DeviceIdentifierStringSerializationHandler; |
| ... | ... | @@ -28,6 +27,8 @@ import com.sap.sailing.expeditionconnector.persistence.PersistenceFactory; |
| 28 | 27 | import com.sap.sailing.server.gateway.serialization.racelog.tracking.DeviceIdentifierJsonHandler; |
| 29 | 28 | import com.sap.sse.ServerInfo; |
| 30 | 29 | import com.sap.sse.common.TypeBasedServiceFinder; |
| 30 | +import com.sap.sse.replication.FullyInitializedReplicableTracker; |
|
| 31 | +import com.sap.sse.replication.ReplicationService; |
|
| 31 | 32 | import com.sap.sse.security.SecurityService; |
| 32 | 33 | import com.sap.sse.security.shared.QualifiedObjectIdentifier; |
| 33 | 34 | import com.sap.sse.security.shared.impl.WildcardPermissionEncoder; |
| ... | ... | @@ -91,10 +92,11 @@ public class Activator implements BundleActivator { |
| 91 | 92 | registrations.add(context.registerService(DeviceIdentifierStringSerializationHandler.class, new ExpeditionSensorStringSerializationHandler(), getDict(ExpeditionSensorDeviceIdentifier.TYPE))); |
| 92 | 93 | |
| 93 | 94 | new Thread(() -> { |
| 94 | - final ServiceTracker<SecurityService, SecurityService> securityServiceServiceTracker = ServiceTrackerFactory |
|
| 95 | - .createAndOpen(context, SecurityService.class); |
|
| 95 | + final FullyInitializedReplicableTracker<SecurityService> securityServiceServiceTracker = new FullyInitializedReplicableTracker<>( |
|
| 96 | + context, SecurityService.class, /* customizer */ null, ServiceTrackerFactory.createAndOpen(context, ReplicationService.class)); |
|
| 97 | + securityServiceServiceTracker.open(); |
|
| 96 | 98 | try { |
| 97 | - final SecurityService securityService = securityServiceServiceTracker.waitForService(0); |
|
| 99 | + final SecurityService securityService = securityServiceServiceTracker.getInitializedService(0); |
|
| 98 | 100 | final WildcardPermissionEncoder permissionEncoder = new WildcardPermissionEncoder(); |
| 99 | 101 | for (ExpeditionDeviceConfiguration deviceConfiguration : expeditionTrackerFactory |
| 100 | 102 | .getDeviceConfigurations()) { |
java/com.sap.sailing.gwt.ui/GWT Sailing SDM.launch
| ... | ... | @@ -116,7 +116,7 @@ |
| 116 | 116 | <stringAttribute key="org.eclipse.jdt.launching.CLASSPATH_PROVIDER" value="com.gwtplugins.gwt.eclipse.core.moduleClasspathProvider"/> |
| 117 | 117 | <booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH" value="false"/> |
| 118 | 118 | <stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="com.google.gwt.dev.DevMode"/> |
| 119 | -<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-style PRETTY -incremental -war "${project_loc:com.sap.sailing.gwt.ui}" -noserver -remoteUI "${gwt_remote_ui_server_port}:${unique_id}" -logLevel INFO -codeServerPort 9876 -bindAddress 0.0.0.0 -startupUrl /gwt/Home.html -startupUrl /gwt/AdminConsole.html -startupUrl /gwt/PairingList.html -startupUrl /gwt/LeaderboardEditing.html -startupUrl /gwt/Leaderboard.html -startupUrl /gwt/Spectator.html -startupUrl /gwt/EmbeddedMapAndWindChart -startupUrl /gwt/RaceBoard.html -startupUrl /gwt/RegattaOverview.html -startupUrl /gwt/DataMining.html -startupUrl /gwt/Simulator.html -startupUrl /gwt/VideoPopup.html -startupUrl /gwt/AutoPlay.html -startupUrl /gwt/YoutubePopup.html com.sap.sailing.gwt.home.Home com.sap.sailing.gwt.ui.AdminConsole com.sap.sailing.gwt.ui.PairingList com.sap.sailing.gwt.ui.LeaderboardEditing com.sap.sailing.gwt.ui.Leaderboard com.sap.sailing.gwt.ui.Spectator com.sap.sailing.gwt.ui.EmbeddedMapAndWindChart com.sap.sailing.gwt.ui.RaceBoard com.sap.sailing.gwt.regattaoverview.RegattaOverview com.sap.sailing.gwt.ui.DataMining com.sap.sailing.gwt.ui.Simulator com.sap.sailing.gwt.ui.VideoPopup com.sap.sailing.gwt.ui.YoutubePopup com.sap.sailing.gwt.autoplay.AutoPlay"/> |
|
| 119 | +<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-style PRETTY -incremental -war "${project_loc:com.sap.sailing.gwt.ui}" -noserver -remoteUI "${gwt_remote_ui_server_port}:${unique_id}" -logLevel INFO -codeServerPort 9876 -startupUrl /gwt/Home.html -startupUrl /gwt/AdminConsole.html -startupUrl /gwt/PairingList.html -startupUrl /gwt/LeaderboardEditing.html -startupUrl /gwt/Leaderboard.html -startupUrl /gwt/Spectator.html -startupUrl /gwt/EmbeddedMapAndWindChart -startupUrl /gwt/RaceBoard.html -startupUrl /gwt/RegattaOverview.html -startupUrl /gwt/DataMining.html -startupUrl /gwt/Simulator.html -startupUrl /gwt/VideoPopup.html -startupUrl /gwt/AutoPlay.html -startupUrl /gwt/YoutubePopup.html com.sap.sailing.gwt.home.Home com.sap.sailing.gwt.ui.AdminConsole com.sap.sailing.gwt.ui.PairingList com.sap.sailing.gwt.ui.LeaderboardEditing com.sap.sailing.gwt.ui.Leaderboard com.sap.sailing.gwt.ui.Spectator com.sap.sailing.gwt.ui.EmbeddedMapAndWindChart com.sap.sailing.gwt.ui.RaceBoard com.sap.sailing.gwt.regattaoverview.RegattaOverview com.sap.sailing.gwt.ui.DataMining com.sap.sailing.gwt.ui.Simulator com.sap.sailing.gwt.ui.VideoPopup com.sap.sailing.gwt.ui.YoutubePopup com.sap.sailing.gwt.autoplay.AutoPlay"/> |
|
| 120 | 120 | <stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="com.sap.sailing.gwt.ui"/> |
| 121 | 121 | <stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-XX:+UseG1GC -XX:+UseStringDeduplication -Dgwt.watchFileChanges=false -Xmx2048m -Dgwt-usearchives=false -Dgwt.persistentunitcache=false"/> |
| 122 | 122 | <stringAttribute key="org.eclipse.jdt.launching.WORKING_DIRECTORY" value="${project_loc:com.sap.sailing.gwt.ui}/.tmp/gwt-work"/> |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/whatsnew/resources/SailingAnalyticsNotes.html
| ... | ... | @@ -9,9 +9,15 @@ |
| 9 | 9 | <li>The security infrastructure of the previously separate environments of |
| 10 | 10 | www.sapsailing.com, dedicated event servers and other sub-domains such as |
| 11 | 11 | club-level or sailing federation servers with their |
| 12 | - own sub-domains has now been merged. Users can now freely roam from specific |
|
| 13 | - servers to www.sapsailing.com and back without losing their user session, |
|
| 14 | - preferences or sailor profiles. One sapsailing.com account does it all.</li> |
|
| 12 | + own sub-domains has now been merged. Users can now roam freely from specific |
|
| 13 | + servers to <a href="https://www.sapsailing.com">www.sapsailing.com</a> and back without losing their user session, |
|
| 14 | + preferences or sailor profiles. One <tt>sapsailing.com</tt> account does it all.</li> |
|
| 15 | + <li>The cross track error values (XTE) for the legs now contain the actual values at the time point for which the |
|
| 16 | + leaderboard is shown when the competitor is sailing in that leg at this time point; after the leg, as in previous |
|
| 17 | + versions, the average values are shown. With this, during a leg you can sort by the current distance to the |
|
| 18 | + leg's course middle line, absolute or signed. Note that this change has no effect on the <em>race-level</em> |
|
| 19 | + cross track error values which are still computed as averages at all times, restricted to the upwind legs of the |
|
| 20 | + race.</li> |
|
| 15 | 21 | </ul> |
| 16 | 22 | |
| 17 | 23 | <h5 class="articleSubheadline">January 2020</h5> |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/server/servlets/SailingDispatchServlet.java
| 18 | 24 | index 8b195ce..9dfff8d |
| 19 | --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/server/servlets/SailingDispatchServlet.java |
|
| 25 | +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/server/servlets/SailingDispatchServlet.java |
|
| ... | ... | @@ -23,35 +23,47 @@ import com.sap.sse.gwt.dispatch.client.transport.gwtrpc.RequestWrapper; |
| 23 | 23 | import com.sap.sse.gwt.dispatch.servlets.AbstractDispatchServlet; |
| 24 | 24 | import com.sap.sse.gwt.dispatch.shared.commands.Action; |
| 25 | 25 | import com.sap.sse.gwt.dispatch.shared.commands.Result; |
| 26 | +import com.sap.sse.replication.FullyInitializedReplicableTracker; |
|
| 27 | +import com.sap.sse.replication.ReplicationService; |
|
| 26 | 28 | import com.sap.sse.security.SecurityService; |
| 27 | 29 | import com.sap.sse.util.ServiceTrackerFactory; |
| 28 | 30 | |
| 29 | 31 | public class SailingDispatchServlet extends AbstractDispatchServlet<SailingDispatchContext> { |
| 30 | 32 | private static final long serialVersionUID = -245230476512348999L; |
| 31 | 33 | |
| 32 | - private final ServiceTracker<RacingEventService, RacingEventService> racingEventServiceTracker; |
|
| 34 | + private final FullyInitializedReplicableTracker<RacingEventService> racingEventServiceTracker; |
|
| 33 | 35 | private final ServiceTracker<WindFinderTrackerFactory, WindFinderTrackerFactory> windFinderTrackerFactory; |
| 34 | 36 | private final ServiceTracker<EventNewsService, EventNewsService> eventNewsServiceTracker; |
| 35 | - private final ServiceTracker<SecurityService, SecurityService> securityServiceTracker; |
|
| 37 | + private final FullyInitializedReplicableTracker<SecurityService> securityServiceTracker; |
|
| 36 | 38 | private final ServiceTracker<TrackedRaceStatisticsCache, TrackedRaceStatisticsCache> trackedRaceStatisticsCacheTracker; |
| 37 | 39 | |
| 38 | 40 | public SailingDispatchServlet() { |
| 39 | 41 | final BundleContext context = Activator.getDefault(); |
| 40 | - racingEventServiceTracker = ServiceTrackerFactory.createAndOpen(context, RacingEventService.class); |
|
| 42 | + final ServiceTracker<ReplicationService, ReplicationService> replicationServiceTracker = |
|
| 43 | + ServiceTrackerFactory.createAndOpen(context, ReplicationService.class); |
|
| 44 | + racingEventServiceTracker = new FullyInitializedReplicableTracker<>(context, RacingEventService.class, |
|
| 45 | + /* customizer */ null, replicationServiceTracker); |
|
| 46 | + racingEventServiceTracker.open(); |
|
| 41 | 47 | windFinderTrackerFactory = ServiceTrackerFactory.createAndOpen(context, WindFinderTrackerFactory.class); |
| 42 | 48 | eventNewsServiceTracker = ServiceTrackerFactory.createAndOpen(context, EventNewsService.class); |
| 43 | - securityServiceTracker = ServiceTrackerFactory.createAndOpen(context, SecurityService.class); |
|
| 49 | + securityServiceTracker = new FullyInitializedReplicableTracker<>(context, SecurityService.class, |
|
| 50 | + /* customizer */ null, replicationServiceTracker); |
|
| 51 | + securityServiceTracker.open(); |
|
| 44 | 52 | trackedRaceStatisticsCacheTracker = ServiceTrackerFactory.createAndOpen(context, TrackedRaceStatisticsCache.class); |
| 45 | 53 | } |
| 46 | 54 | |
| 47 | 55 | @Override |
| 48 | 56 | protected <R extends Result, A extends Action<R, SailingDispatchContext>> SailingDispatchContext createDispatchContextFor( |
| 49 | 57 | RequestWrapper<R, A, SailingDispatchContext> request) { |
| 50 | - return new SailingDispatchContextImpl(request.getCurrentClientTime(), racingEventServiceTracker.getService(), |
|
| 51 | - windFinderTrackerFactory.getService(), |
|
| 52 | - eventNewsServiceTracker.getService(), securityServiceTracker.getService(), |
|
| 53 | - trackedRaceStatisticsCacheTracker.getService(), |
|
| 54 | - request.getClientLocaleName(), getThreadLocalRequest()); |
|
| 58 | + try { |
|
| 59 | + return new SailingDispatchContextImpl(request.getCurrentClientTime(), racingEventServiceTracker.getInitializedService(0), |
|
| 60 | + windFinderTrackerFactory.getService(), |
|
| 61 | + eventNewsServiceTracker.getService(), securityServiceTracker.getInitializedService(0), |
|
| 62 | + trackedRaceStatisticsCacheTracker.getService(), |
|
| 63 | + request.getClientLocaleName(), getThreadLocalRequest()); |
|
| 64 | + } catch (InterruptedException e) { |
|
| 65 | + throw new RuntimeException(e); |
|
| 66 | + } |
|
| 55 | 67 | } |
| 56 | 68 | |
| 57 | 69 | @Override |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/server/ServerConfigurationServiceImpl.java
| ... | ... | @@ -1,12 +1,13 @@ |
| 1 | 1 | package com.sap.sailing.gwt.server; |
| 2 | 2 | |
| 3 | 3 | import org.osgi.framework.BundleContext; |
| 4 | -import org.osgi.util.tracker.ServiceTracker; |
|
| 5 | 4 | |
| 6 | 5 | import com.sap.sailing.gwt.ui.client.ServerConfigurationService; |
| 7 | 6 | import com.sap.sailing.gwt.ui.server.Activator; |
| 8 | 7 | import com.sap.sailing.server.interfaces.RacingEventService; |
| 9 | 8 | import com.sap.sse.gwt.server.ProxiedRemoteServiceServlet; |
| 9 | +import com.sap.sse.replication.FullyInitializedReplicableTracker; |
|
| 10 | +import com.sap.sse.replication.ReplicationService; |
|
| 10 | 11 | import com.sap.sse.util.ServiceTrackerFactory; |
| 11 | 12 | |
| 12 | 13 | /** |
| ... | ... | @@ -15,15 +16,21 @@ import com.sap.sse.util.ServiceTrackerFactory; |
| 15 | 16 | public class ServerConfigurationServiceImpl extends ProxiedRemoteServiceServlet implements ServerConfigurationService { |
| 16 | 17 | private static final long serialVersionUID = 2571063542941630865L; |
| 17 | 18 | |
| 18 | - private final ServiceTracker<RacingEventService, RacingEventService> racingEventServiceTracker; |
|
| 19 | + private final FullyInitializedReplicableTracker<RacingEventService> racingEventServiceTracker; |
|
| 19 | 20 | |
| 20 | 21 | public ServerConfigurationServiceImpl() { |
| 21 | 22 | BundleContext context = Activator.getDefault(); |
| 22 | - racingEventServiceTracker = ServiceTrackerFactory.createAndOpen(context, RacingEventService.class); |
|
| 23 | + racingEventServiceTracker = new FullyInitializedReplicableTracker<>(context, RacingEventService.class, |
|
| 24 | + /* customizer */ null, ServiceTrackerFactory.createAndOpen(context, ReplicationService.class)); |
|
| 25 | + racingEventServiceTracker.open(); |
|
| 23 | 26 | } |
| 24 | 27 | |
| 25 | 28 | protected RacingEventService getService() { |
| 26 | - return racingEventServiceTracker.getService(); |
|
| 29 | + try { |
|
| 30 | + return racingEventServiceTracker.getInitializedService(0); |
|
| 31 | + } catch (InterruptedException e) { |
|
| 32 | + throw new RuntimeException(e); |
|
| 33 | + } |
|
| 27 | 34 | } |
| 28 | 35 | |
| 29 | 36 | @Override |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties
| ... | ... | @@ -367,9 +367,9 @@ leaderboardsInGroup=Leaderboards in group |
| 367 | 367 | replication=Replication |
| 368 | 368 | errorFetchingReplicaData=Error fetching replica data: {0} |
| 369 | 369 | averageAbsoluteCrossTrackErrorInMeters=\u2205 XTE |
| 370 | -averageAbsoluteCrossTrackErrorInMetersTooltip=The average cross track error to the great circle segment connecting leg start with leg end |
|
| 370 | +averageAbsoluteCrossTrackErrorInMetersTooltip=The average cross track error to the great circle segment connecting leg start with leg end; shows the average after the leg, and the current value while in the leg. |
|
| 371 | 371 | averageSignedCrossTrackErrorInMeters=\u2205 XTE +/- |
| 372 | -averageSignedCrossTrackErrorInMetersTooltip=The average position on the course as a signed distance from the course middle line; 0 meaning on the course middle line; negative values mean left-hand side, positive values mean right-hand side. |
|
| 372 | +averageSignedCrossTrackErrorInMetersTooltip=The average position on the course as a signed distance from the course middle line; 0 meaning on the course middle line; negative values mean left-hand side, positive values mean right-hand side; shows the average after the leg, and the current value while in the leg. |
|
| 373 | 373 | raceAverageAbsoluteCrossTrackErrorInMetersTooltip=The average absolute cross track error to the great circle segments connecting leg start with leg end for all upwind legs of this race, ignoring to which side of the course middle line the competitor is |
| 374 | 374 | raceAverageSignedCrossTrackErrorInMetersTooltip=The signed average cross track error to the great circle segments connecting leg start with leg end for all upwind legs of this race; negative values mean left of the course middle line, positive values mean right. |
| 375 | 375 | enterMaster=Hostname of Master Instance |
| ... | ... | @@ -2246,7 +2246,7 @@ migrateCompetitors=Migrate competitors |
| 2246 | 2246 | migrateBoats=Migrate boats |
| 2247 | 2247 | migrateHierarchyToGroupOwner=Migrate hierarchy to group owner |
| 2248 | 2248 | serverIsPublicButTenantIsNot=This server is configured as public, your current tenant is not the server ones. Objects created by you will not be public without further server configuration. Change your default tenant via the user profile if you want a default public server scenario. |
| 2249 | -serverIsPublicButTenantIsNotAndCouldBeChanged=This server is configured as public, your current tenant is not the server ones. Objects created by you will not be public without further server configuration.Should we change the default tenant for you? |
|
| 2249 | +serverIsPublicButTenantIsNotAndCouldBeChanged=This server is configured as public, your current tenant is not the server ones. Objects created by you will not be public without further server configuration. Should we change the default tenant for you? |
|
| 2250 | 2250 | copyMembersAndRoles=Copy members and roles |
| 2251 | 2251 | invalidSecret=Invalid Secret |
| 2252 | 2252 | warningSailInsightVersion=The server is configured for use with SAP Sail Insight 1. Open regattas are only supported in SAP Sail Insight 2. This can cause problems in use |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties
| ... | ... | @@ -365,8 +365,8 @@ leaderboardsInGroup=Ranglisten in der Gruppe |
| 365 | 365 | replication=Replikation |
| 366 | 366 | errorFetchingReplicaData=Fehler beim Beschaffen der Replikationsdaten: {0} |
| 367 | 367 | averageAbsoluteCrossTrackErrorInMeters=\u2205 XTE |
| 368 | -averageAbsoluteCrossTrackErrorInMetersTooltip=Die durchschnittliche Querabweichung zur Kursmittellinie in diesem Schenkel; die Seite, zu der der Teilnehmer abweicht, spielt dabei keine Rolle. |
|
| 369 | -raceAverageAbsoluteCrossTrackErrorInMetersTooltip=Die durchschnittliche Querabweichung zur Kursmittellinie für alle Kreuzen in diesem Rennen; die Seite, zu der der Teilnehmer abweicht, spielt dabei keine Rolle. |
|
| 368 | +averageAbsoluteCrossTrackErrorInMetersTooltip=Die durchschnittliche Querabweichung zur Kursmittellinie in diesem Schenkel; die Seite, zu der der Teilnehmer abweicht, spielt dabei keine Rolle; aktueller Wert während des Bahnschenkels, danach der Durchschnittswert. |
|
| 369 | +raceAverageAbsoluteCrossTrackErrorInMetersTooltip=Die durchschnittliche Querabweichung zur Kursmittellinie für alle Kreuzen in diesem Rennen; die Seite, zu der der Teilnehmer abweicht, spielt dabei keine Rolle; aktueller Wert während des Bahnschenkels, danach der Durchschnittswert. |
|
| 370 | 370 | averageSignedCrossTrackErrorInMeters=\u2205 XTE +/- |
| 371 | 371 | averageSignedCrossTrackErrorInMetersTooltip=Die durchschnittliche Querabweichung zur Kursmittellinie in diesem Schenkel, wobei negative Werte Abweichungen nach links, positive Werte Abweichungen nach rechts bedeuten. |
| 372 | 372 | raceAverageSignedCrossTrackErrorInMetersTooltip=Die durchschnittliche Querabweichung zur Kursmittellinie für alle Kreuzen diesem Rennen, wobei negative Werte Abweichungen nach links, positive Werte Abweichungen nach rechts bedeuten. |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LegColumn.java
| ... | ... | @@ -268,11 +268,11 @@ public class LegColumn extends ExpandableSortableColumn<String> { |
| 268 | 268 | detailColumnStyle, leaderboardPanel)); |
| 269 | 269 | result.put(DetailType.LEG_AVERAGE_ABSOLUTE_CROSS_TRACK_ERROR_IN_METERS, |
| 270 | 270 | new FormattedDoubleLeaderboardRowDTODetailTypeColumn(DetailType.LEG_AVERAGE_ABSOLUTE_CROSS_TRACK_ERROR_IN_METERS, |
| 271 | - new DoubleDetailTypeExtractor(e -> e.averageAbsoluteCrossTrackErrorInMeters), detailHeaderStyle, |
|
| 271 | + new DoubleDetailTypeExtractor(e -> e.currentOrAverageAbsoluteCrossTrackErrorInMeters), detailHeaderStyle, |
|
| 272 | 272 | detailColumnStyle, leaderboardPanel)); |
| 273 | 273 | result.put(DetailType.LEG_AVERAGE_SIGNED_CROSS_TRACK_ERROR_IN_METERS, |
| 274 | 274 | new FormattedDoubleLeaderboardRowDTODetailTypeColumn(DetailType.LEG_AVERAGE_SIGNED_CROSS_TRACK_ERROR_IN_METERS, |
| 275 | - new DoubleDetailTypeExtractor(e -> e.averageSignedCrossTrackErrorInMeters), detailHeaderStyle, |
|
| 275 | + new DoubleDetailTypeExtractor(e -> e.currentOrAverageSignedCrossTrackErrorInMeters), detailHeaderStyle, |
|
| 276 | 276 | detailColumnStyle, leaderboardPanel)); |
| 277 | 277 | result.put(DetailType.EXPEDITION_LEG_AWA, |
| 278 | 278 | new FormattedDoubleLeaderboardRowDTODetailTypeColumn(DetailType.EXPEDITION_LEG_AWA, |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/DataMiningServiceImpl.java
| ... | ... | @@ -49,6 +49,8 @@ import com.sap.sse.datamining.shared.impl.dto.StoredDataMiningQueryDTOImpl; |
| 49 | 49 | import com.sap.sse.datamining.ui.client.DataMiningService; |
| 50 | 50 | import com.sap.sse.gwt.server.ProxiedRemoteServiceServlet; |
| 51 | 51 | import com.sap.sse.i18n.ResourceBundleStringMessages; |
| 52 | +import com.sap.sse.replication.FullyInitializedReplicableTracker; |
|
| 53 | +import com.sap.sse.replication.ReplicationService; |
|
| 52 | 54 | import com.sap.sse.security.SecurityService; |
| 53 | 55 | import com.sap.sse.security.shared.impl.SecuredSecurityTypes.ServerActions; |
| 54 | 56 | import com.sap.sse.util.ServiceTrackerFactory; |
| ... | ... | @@ -58,14 +60,16 @@ public class DataMiningServiceImpl extends ProxiedRemoteServiceServlet implement |
| 58 | 60 | |
| 59 | 61 | private final BundleContext context; |
| 60 | 62 | private final ServiceTracker<DataMiningServer, DataMiningServer> dataMiningServerTracker; |
| 61 | - private final ServiceTracker<SecurityService, SecurityService> securityServiceTracker; |
|
| 63 | + private final FullyInitializedReplicableTracker<SecurityService> securityServiceTracker; |
|
| 62 | 64 | private final StoredDataMiningQueryPersister storedDataMiningQueryPersistor; |
| 63 | 65 | private final DataMiningDTOFactory dtoFactory; |
| 64 | 66 | |
| 65 | 67 | public DataMiningServiceImpl() { |
| 66 | 68 | context = Activator.getDefault(); |
| 67 | 69 | dataMiningServerTracker = createAndOpenDataMiningServerTracker(context); |
| 68 | - securityServiceTracker = ServiceTrackerFactory.createAndOpen(context, SecurityService.class); |
|
| 70 | + securityServiceTracker = new FullyInitializedReplicableTracker<>(context, SecurityService.class, |
|
| 71 | + /* customizer */ null, ServiceTrackerFactory.createAndOpen(context, ReplicationService.class)); |
|
| 72 | + securityServiceTracker.open(); |
|
| 69 | 73 | storedDataMiningQueryPersistor = new StoredDataMiningQueryPersisterImpl(getSecurityService(), |
| 70 | 74 | dataMiningServerTracker); |
| 71 | 75 | dtoFactory = new DataMiningDTOFactory(); |
| ... | ... | @@ -84,7 +88,11 @@ public class DataMiningServiceImpl extends ProxiedRemoteServiceServlet implement |
| 84 | 88 | } |
| 85 | 89 | |
| 86 | 90 | private SecurityService getSecurityService() { |
| 87 | - return securityServiceTracker.getService(); |
|
| 91 | + try { |
|
| 92 | + return securityServiceTracker.getInitializedService(0); |
|
| 93 | + } catch (InterruptedException e) { |
|
| 94 | + throw new RuntimeException(e); |
|
| 95 | + } |
|
| 88 | 96 | } |
| 89 | 97 | |
| 90 | 98 | @Override |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java
| ... | ... | @@ -394,7 +394,6 @@ import com.sap.sailing.domain.regattalike.LeaderboardThatHasRegattaLike; |
| 394 | 394 | import com.sap.sailing.domain.regattalog.RegattaLogStore; |
| 395 | 395 | import com.sap.sailing.domain.resultimport.ResultUrlProvider; |
| 396 | 396 | import com.sap.sailing.domain.sharding.ShardingContext; |
| 397 | -import com.sap.sailing.domain.sharedsailingdata.SharedSailingData; |
|
| 398 | 397 | import com.sap.sailing.domain.swisstimingadapter.StartList; |
| 399 | 398 | import com.sap.sailing.domain.swisstimingadapter.SwissTimingAdapter; |
| 400 | 399 | import com.sap.sailing.domain.swisstimingadapter.SwissTimingAdapterFactory; |
| ... | ... | @@ -601,6 +600,7 @@ import com.sap.sailing.server.operationaltransformation.UpdateSpecificRegatta; |
| 601 | 600 | import com.sap.sailing.server.security.PermissionAwareRaceTrackingHandler; |
| 602 | 601 | import com.sap.sailing.server.security.SailingViewerRole; |
| 603 | 602 | import com.sap.sailing.server.util.WaitForTrackedRaceUtil; |
| 603 | +import com.sap.sailing.shared.server.SharedSailingData; |
|
| 604 | 604 | import com.sap.sailing.simulator.Path; |
| 605 | 605 | import com.sap.sailing.simulator.PolarDiagram; |
| 606 | 606 | import com.sap.sailing.simulator.SimulationResults; |
| ... | ... | @@ -658,6 +658,7 @@ import com.sap.sse.pairinglist.PairingList; |
| 658 | 658 | import com.sap.sse.pairinglist.PairingListTemplate; |
| 659 | 659 | import com.sap.sse.pairinglist.impl.PairingListTemplateImpl; |
| 660 | 660 | import com.sap.sse.qrcode.QRCodeGenerationUtil; |
| 661 | +import com.sap.sse.replication.FullyInitializedReplicableTracker; |
|
| 661 | 662 | import com.sap.sse.replication.OperationWithResult; |
| 662 | 663 | import com.sap.sse.replication.ReplicaDescriptor; |
| 663 | 664 | import com.sap.sse.replication.Replicable; |
| ... | ... | @@ -706,7 +707,7 @@ public class SailingServiceImpl extends ResultCachingProxiedRemoteServiceServlet |
| 706 | 707 | |
| 707 | 708 | private static final long serialVersionUID = 9031688830194537489L; |
| 708 | 709 | |
| 709 | - private final ServiceTracker<RacingEventService, RacingEventService> racingEventServiceTracker; |
|
| 710 | + private final FullyInitializedReplicableTracker<RacingEventService> racingEventServiceTracker; |
|
| 710 | 711 | |
| 711 | 712 | private final ServiceTracker<ReplicationService, ReplicationService> replicationServiceTracker; |
| 712 | 713 | |
| ... | ... | @@ -733,9 +734,9 @@ public class SailingServiceImpl extends ResultCachingProxiedRemoteServiceServlet |
| 733 | 734 | private final ServiceTracker<DeviceIdentifierStringSerializationHandler, DeviceIdentifierStringSerializationHandler> |
| 734 | 735 | deviceIdentifierStringSerializationHandlerTracker; |
| 735 | 736 | |
| 736 | - private final ServiceTracker<SecurityService, SecurityService> securityServiceTracker; |
|
| 737 | + private final FullyInitializedReplicableTracker<SecurityService> securityServiceTracker; |
|
| 737 | 738 | |
| 738 | - private final ServiceTracker<SharedSailingData, SharedSailingData> sharedSailingDataTracker; |
|
| 739 | + private final FullyInitializedReplicableTracker<SharedSailingData> sharedSailingDataTracker; |
|
| 739 | 740 | |
| 740 | 741 | private final com.sap.sailing.domain.tractracadapter.persistence.MongoObjectFactory tractracMongoObjectFactory; |
| 741 | 742 | |
| ... | ... | @@ -778,17 +779,23 @@ public class SailingServiceImpl extends ResultCachingProxiedRemoteServiceServlet |
| 778 | 779 | BundleContext context = Activator.getDefault(); |
| 779 | 780 | Activator activator = Activator.getInstance(); |
| 780 | 781 | quickRanksLiveCache = new QuickRanksLiveCache(this); |
| 781 | - racingEventServiceTracker = ServiceTrackerFactory.createAndOpen(context, RacingEventService.class); |
|
| 782 | - sharedSailingDataTracker = ServiceTrackerFactory.createAndOpen(context, SharedSailingData.class); |
|
| 783 | - windFinderTrackerFactoryServiceTracker = ServiceTrackerFactory.createAndOpen(context, WindFinderTrackerFactory.class); |
|
| 784 | 782 | replicationServiceTracker = ServiceTrackerFactory.createAndOpen(context, ReplicationService.class); |
| 783 | + racingEventServiceTracker = new FullyInitializedReplicableTracker<>(context, RacingEventService.class, |
|
| 784 | + /* customizer */ null, replicationServiceTracker); |
|
| 785 | + racingEventServiceTracker.open(); |
|
| 786 | + sharedSailingDataTracker = new FullyInitializedReplicableTracker<>(context, SharedSailingData.class, |
|
| 787 | + /* service tracker customizer */ null, replicationServiceTracker); |
|
| 788 | + sharedSailingDataTracker.open(); |
|
| 789 | + windFinderTrackerFactoryServiceTracker = ServiceTrackerFactory.createAndOpen(context, WindFinderTrackerFactory.class); |
|
| 785 | 790 | swissTimingAdapterTracker = ServiceTrackerFactory.createAndOpen(context, SwissTimingAdapterFactory.class); |
| 786 | 791 | tractracAdapterTracker = ServiceTrackerFactory.createAndOpen(context, TracTracAdapterFactory.class); |
| 787 | 792 | raceLogTrackingAdapterTracker = ServiceTrackerFactory.createAndOpen(context, |
| 788 | 793 | RaceLogTrackingAdapterFactory.class); |
| 789 | 794 | deviceIdentifierStringSerializationHandlerTracker = ServiceTrackerFactory.createAndOpen(context, |
| 790 | 795 | DeviceIdentifierStringSerializationHandler.class); |
| 791 | - securityServiceTracker = ServiceTrackerFactory.createAndOpen(context, SecurityService.class); |
|
| 796 | + securityServiceTracker = new FullyInitializedReplicableTracker<>(context, SecurityService.class, |
|
| 797 | + /* service tracker customizer */ null, replicationServiceTracker); |
|
| 798 | + securityServiceTracker.open(); |
|
| 792 | 799 | igtimiAdapterTracker = ServiceTrackerFactory.createAndOpen(context, IgtimiConnectionFactory.class); |
| 793 | 800 | baseDomainFactory = getService().getBaseDomainFactory(); |
| 794 | 801 | mongoObjectFactory = getService().getMongoObjectFactory(); |
| ... | ... | @@ -2776,7 +2783,7 @@ public class SailingServiceImpl extends ResultCachingProxiedRemoteServiceServlet |
| 2776 | 2783 | |
| 2777 | 2784 | protected RacingEventService getService() { |
| 2778 | 2785 | try { |
| 2779 | - return racingEventServiceTracker.waitForService(0); |
|
| 2786 | + return racingEventServiceTracker.getInitializedService(0); |
|
| 2780 | 2787 | } catch (InterruptedException e) { |
| 2781 | 2788 | throw new RuntimeException(e); |
| 2782 | 2789 | } // grab the service |
| ... | ... | @@ -2784,7 +2791,7 @@ public class SailingServiceImpl extends ResultCachingProxiedRemoteServiceServlet |
| 2784 | 2791 | |
| 2785 | 2792 | protected SharedSailingData getSharedSailingData() { |
| 2786 | 2793 | try { |
| 2787 | - return sharedSailingDataTracker.waitForService(0); |
|
| 2794 | + return sharedSailingDataTracker.getInitializedService(0); |
|
| 2788 | 2795 | } catch (InterruptedException e) { |
| 2789 | 2796 | throw new RuntimeException(e); |
| 2790 | 2797 | } // grab the service |
| ... | ... | @@ -2800,7 +2807,7 @@ public class SailingServiceImpl extends ResultCachingProxiedRemoteServiceServlet |
| 2800 | 2807 | |
| 2801 | 2808 | protected SecurityService getSecurityService() { |
| 2802 | 2809 | try { |
| 2803 | - return securityServiceTracker.waitForService(0); |
|
| 2810 | + return securityServiceTracker.getInitializedService(0); |
|
| 2804 | 2811 | } catch (InterruptedException e) { |
| 2805 | 2812 | throw new RuntimeException(e); |
| 2806 | 2813 | } |
| ... | ... | @@ -4467,12 +4474,17 @@ public class SailingServiceImpl extends ResultCachingProxiedRemoteServiceServlet |
| 4467 | 4474 | // The queue name must always be the same for this server. In order to achieve |
| 4468 | 4475 | // this we're using the unique server identifier |
| 4469 | 4476 | final ReplicationService replicationService = getReplicationService(); |
| 4470 | - replicationService.startToReplicateFrom(replicationService.createReplicationMasterDescriptor(messagingHost, |
|
| 4471 | - masterHostName, exchangeName, servletPort, messagingPort, |
|
| 4472 | - /* use local server identifier as queue name */ replicationService.getServerIdentifier().toString(), |
|
| 4473 | - RemoteServerUtil.resolveBearerTokenForRemoteServer(masterHostName, servletPort, usernameOrNull, |
|
| 4474 | - passwordOrNull), |
|
| 4475 | - replicationService.getAllReplicables())); |
|
| 4477 | + replicationService.setReplicationStarting(true); |
|
| 4478 | + try { |
|
| 4479 | + replicationService.startToReplicateFrom(replicationService.createReplicationMasterDescriptor(messagingHost, |
|
| 4480 | + masterHostName, exchangeName, servletPort, messagingPort, |
|
| 4481 | + /* use local server identifier as queue name */ replicationService.getServerIdentifier().toString(), |
|
| 4482 | + RemoteServerUtil.resolveBearerTokenForRemoteServer(masterHostName, servletPort, usernameOrNull, |
|
| 4483 | + passwordOrNull), |
|
| 4484 | + replicationService.getAllReplicables())); |
|
| 4485 | + } finally { |
|
| 4486 | + replicationService.setReplicationStarting(false); |
|
| 4487 | + } |
|
| 4476 | 4488 | } |
| 4477 | 4489 | |
| 4478 | 4490 | @Override |
| ... | ... | @@ -5324,15 +5336,15 @@ public class SailingServiceImpl extends ResultCachingProxiedRemoteServiceServlet |
| 5324 | 5336 | @Override |
| 5325 | 5337 | public List<UrlDTO> getResultImportUrls(String resultProviderName) { |
| 5326 | 5338 | final List<UrlDTO> result = new ArrayList<>(); |
| 5327 | - SecurityService securityService = getSecurityService(); |
|
| 5339 | + SecurityService securityService = getSecurityService(); |
|
| 5328 | 5340 | Iterable<URL> allUrlsReadableBySubject = getService().getResultImportUrls(resultProviderName); |
| 5329 | - for (URL url : allUrlsReadableBySubject) { |
|
| 5330 | - QualifiedObjectIdentifier objId = SecuredDomainType.RESULT_IMPORT_URL.getQualifiedObjectIdentifier( |
|
| 5341 | + for (URL url : allUrlsReadableBySubject) { |
|
| 5342 | + QualifiedObjectIdentifier objId = SecuredDomainType.RESULT_IMPORT_URL.getQualifiedObjectIdentifier( |
|
| 5331 | 5343 | new TypeRelativeObjectIdentifier(resultProviderName, url.toString())); |
| 5332 | - UrlDTO urlDTO = new UrlDTO(url.toString()); |
|
| 5333 | - SecurityDTOUtil.addSecurityInformation(securityService, urlDTO, objId); |
|
| 5334 | - result.add(urlDTO); |
|
| 5335 | - } |
|
| 5344 | + UrlDTO urlDTO = new UrlDTO(url.toString()); |
|
| 5345 | + SecurityDTOUtil.addSecurityInformation(securityService, urlDTO, objId); |
|
| 5346 | + result.add(urlDTO); |
|
| 5347 | + } |
|
| 5336 | 5348 | return result; |
| 5337 | 5349 | } |
| 5338 | 5350 | |
| ... | ... | @@ -5345,12 +5357,12 @@ public class SailingServiceImpl extends ResultCachingProxiedRemoteServiceServlet |
| 5345 | 5357 | return new URL(urlDto.getUrl()); |
| 5346 | 5358 | } catch (MalformedURLException e) { |
| 5347 | 5359 | return null; |
| 5348 | - } |
|
| 5360 | + } |
|
| 5349 | 5361 | }) |
| 5350 | 5362 | .filter(url -> url != null) |
| 5351 | 5363 | .collect(Collectors.toSet()); |
| 5352 | 5364 | racingEventService.removeResultImportURLs(resultProviderName, urlsToRemove); |
| 5353 | - } |
|
| 5365 | + } |
|
| 5354 | 5366 | |
| 5355 | 5367 | @Override |
| 5356 | 5368 | public void addResultImportUrl(String resultProviderName, UrlDTO urlDTO) throws Exception { |
| ... | ... | @@ -5359,7 +5371,7 @@ public class SailingServiceImpl extends ResultCachingProxiedRemoteServiceServlet |
| 5359 | 5371 | .orElseThrow(() -> new IllegalStateException("ResultUrlProvider not found: " + resultProviderName)); |
| 5360 | 5372 | final URL url = resultUrlProvider.resolveUrl(urlDTO.getUrl()); |
| 5361 | 5373 | racingEventService.addResultImportUrl(resultProviderName, url); |
| 5362 | - } |
|
| 5374 | + } |
|
| 5363 | 5375 | |
| 5364 | 5376 | @Override |
| 5365 | 5377 | public String validateResultImportUrl(String resultProviderName, UrlDTO urlDTO) { |
java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java
| ... | ... | @@ -84,7 +84,7 @@ public class PolarDataServiceImpl implements ReplicablePolarService, ClearStateT |
| 84 | 84 | |
| 85 | 85 | private final Set<OperationWithResult<PolarDataService, ?>> operationsSentToMasterForReplication; |
| 86 | 86 | |
| 87 | - private ThreadLocal<Boolean> currentlyFillingFromInitialLoad = ThreadLocal.withInitial(() -> false); |
|
| 87 | + private volatile boolean currentlyFillingFromInitialLoad; |
|
| 88 | 88 | |
| 89 | 89 | private ThreadLocal<Boolean> currentlyApplyingOperationReceivedFromMaster = ThreadLocal.withInitial(() -> false); |
| 90 | 90 | |
| ... | ... | @@ -103,6 +103,7 @@ public class PolarDataServiceImpl implements ReplicablePolarService, ClearStateT |
| 103 | 103 | */ |
| 104 | 104 | public PolarDataServiceImpl() { |
| 105 | 105 | resetState(); |
| 106 | + this.currentlyFillingFromInitialLoad = false; |
|
| 106 | 107 | this.operationsSentToMasterForReplication = new HashSet<>(); |
| 107 | 108 | this.operationExecutionListeners = new ConcurrentHashMap<>(); |
| 108 | 109 | } |
| ... | ... | @@ -322,12 +323,12 @@ public class PolarDataServiceImpl implements ReplicablePolarService, ClearStateT |
| 322 | 323 | |
| 323 | 324 | @Override |
| 324 | 325 | public boolean isCurrentlyFillingFromInitialLoad() { |
| 325 | - return currentlyFillingFromInitialLoad.get(); |
|
| 326 | + return currentlyFillingFromInitialLoad; |
|
| 326 | 327 | } |
| 327 | 328 | |
| 328 | 329 | @Override |
| 329 | 330 | public void setCurrentlyFillingFromInitialLoad(boolean currentlyFillingFromInitialLoad) { |
| 330 | - this.currentlyFillingFromInitialLoad.set(currentlyFillingFromInitialLoad); |
|
| 331 | + this.currentlyFillingFromInitialLoad = currentlyFillingFromInitialLoad; |
|
| 331 | 332 | } |
| 332 | 333 | |
| 333 | 334 | @Override |
java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/api/test/AclRevokeAnonymousTest.java
| ... | ... | @@ -0,0 +1,64 @@ |
| 1 | +package com.sap.sailing.selenium.api.test; |
|
| 2 | + |
|
| 3 | +import static com.sap.sailing.selenium.api.core.ApiContext.SECURITY_CONTEXT; |
|
| 4 | +import static com.sap.sailing.selenium.api.core.ApiContext.SERVER_CONTEXT; |
|
| 5 | +import static com.sap.sailing.selenium.api.core.ApiContext.createAdminApiContext; |
|
| 6 | +import static com.sap.sailing.selenium.api.core.ApiContext.createApiContext; |
|
| 7 | +import static com.sap.sailing.selenium.pages.adminconsole.AdminConsolePage.goToPage; |
|
| 8 | + |
|
| 9 | +import org.hamcrest.Matchers; |
|
| 10 | +import org.junit.Assert; |
|
| 11 | +import org.junit.Before; |
|
| 12 | +import org.junit.Test; |
|
| 13 | +import org.openqa.selenium.WebElement; |
|
| 14 | + |
|
| 15 | +import com.sap.sailing.domain.common.CompetitorRegistrationType; |
|
| 16 | +import com.sap.sailing.selenium.api.core.ApiContext; |
|
| 17 | +import com.sap.sailing.selenium.api.event.EventApi; |
|
| 18 | +import com.sap.sailing.selenium.api.event.SecurityApi; |
|
| 19 | +import com.sap.sailing.selenium.pages.adminconsole.AclPopupPO; |
|
| 20 | +import com.sap.sailing.selenium.pages.adminconsole.AdminConsolePage; |
|
| 21 | +import com.sap.sailing.selenium.pages.adminconsole.event.EventConfigurationPanelPO; |
|
| 22 | +import com.sap.sailing.selenium.pages.adminconsole.event.EventConfigurationPanelPO.EventEntryPO; |
|
| 23 | +import com.sap.sailing.selenium.test.AbstractSeleniumTest; |
|
| 24 | + |
|
| 25 | +public class AclRevokeAnonymousTest extends AbstractSeleniumTest { |
|
| 26 | + |
|
| 27 | + private ApiContext ownerCtx; |
|
| 28 | + |
|
| 29 | + private final SecurityApi securityApi = new SecurityApi(); |
|
| 30 | + private final EventApi eventApi = new EventApi(); |
|
| 31 | + |
|
| 32 | + private static final String EVENT_NAME = "Super exclusive regatta - invited competitors only"; |
|
| 33 | + private static final String BOAT_CLASS = "GC 32"; |
|
| 34 | + private AdminConsolePage adminConsole; |
|
| 35 | + |
|
| 36 | + @Before |
|
| 37 | + public void setUp() { |
|
| 38 | + clearState(getContextRoot()); |
|
| 39 | + super.setUp(); |
|
| 40 | + final ApiContext adminSecurityCtx = createAdminApiContext(getContextRoot(), SECURITY_CONTEXT); |
|
| 41 | + securityApi.createUser(adminSecurityCtx, "donald", "Donald Duck", null, "daisy0815"); |
|
| 42 | + ownerCtx = createApiContext(getContextRoot(), SERVER_CONTEXT, "donald", "daisy0815"); |
|
| 43 | + adminConsole = goToPage(getWebDriver(), getContextRoot()); |
|
| 44 | + adminConsole.goToLocalServerPanel().setSelfServiceServer(true); |
|
| 45 | + eventApi.createEvent(ownerCtx, EVENT_NAME, BOAT_CLASS, CompetitorRegistrationType.CLOSED, "Some special place"); |
|
| 46 | + } |
|
| 47 | + |
|
| 48 | + @Test |
|
| 49 | + public void test() { |
|
| 50 | + EventConfigurationPanelPO eventPanel = adminConsole.goToEvents(); |
|
| 51 | + EventEntryPO eventEntry = eventPanel.getEventEntry(EVENT_NAME); |
|
| 52 | + AclPopupPO aclPopup = eventEntry.openAclPopup(); |
|
| 53 | + aclPopup.addUserGroup(""); // add empty user group -> anonymous group |
|
| 54 | + WebElement deniedInput = aclPopup.getDeniedActionsInput(); |
|
| 55 | + WebElement allowedInput = aclPopup.getAllowedActionsInput(); |
|
| 56 | + Assert.assertThat(deniedInput.isEnabled(), Matchers.equalTo(false)); |
|
| 57 | + Assert.assertThat(allowedInput.isEnabled(), Matchers.equalTo(true)); |
|
| 58 | + |
|
| 59 | + aclPopup.addUserGroup("admin-tenant"); |
|
| 60 | + Assert.assertThat(deniedInput.isEnabled(), Matchers.equalTo(true)); |
|
| 61 | + Assert.assertThat(allowedInput.isEnabled(), Matchers.equalTo(true)); |
|
| 62 | + } |
|
| 63 | + |
|
| 64 | +} |
java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/pages/adminconsole/AclPopupPO.java
| ... | ... | @@ -0,0 +1,36 @@ |
| 1 | +package com.sap.sailing.selenium.pages.adminconsole; |
|
| 2 | + |
|
| 3 | +import org.openqa.selenium.WebDriver; |
|
| 4 | +import org.openqa.selenium.WebElement; |
|
| 5 | + |
|
| 6 | +import com.sap.sailing.selenium.core.BySeleniumId; |
|
| 7 | +import com.sap.sailing.selenium.pages.PageArea; |
|
| 8 | + |
|
| 9 | +public class AclPopupPO extends PageArea { |
|
| 10 | + |
|
| 11 | + public static final String INPUT_SUGGEST_BOX = "InputSuggestBox"; |
|
| 12 | + public static final String ALLOWED_ACTIONS_CONTAINER = "allowedActionsContainer"; |
|
| 13 | + public static final String DENIED_ACTIONS_CONTAINER = "deniedActionsContainer"; |
|
| 14 | + public static final String ADD_USER_GROUP_BUTTON = "AddUserGroupButton"; |
|
| 15 | + public static final String SUGGEST_USER_GROUP_INPUT = "SuggestUserGroupInput"; |
|
| 16 | + |
|
| 17 | + public AclPopupPO(WebDriver driver, WebElement root) { |
|
| 18 | + super(driver, root); |
|
| 19 | + } |
|
| 20 | + |
|
| 21 | + public void addUserGroup(String groupName) { |
|
| 22 | + driver.findElement(new BySeleniumId(SUGGEST_USER_GROUP_INPUT)).sendKeys(groupName); |
|
| 23 | + driver.findElement(new BySeleniumId(ADD_USER_GROUP_BUTTON)).click(); |
|
| 24 | + } |
|
| 25 | + |
|
| 26 | + public WebElement getDeniedActionsInput() { |
|
| 27 | + WebElement parent = context.findElement(new BySeleniumId(DENIED_ACTIONS_CONTAINER)); |
|
| 28 | + return parent.findElement(new BySeleniumId(INPUT_SUGGEST_BOX)); |
|
| 29 | + } |
|
| 30 | + |
|
| 31 | + public WebElement getAllowedActionsInput() { |
|
| 32 | + WebElement parent = context.findElement(new BySeleniumId(ALLOWED_ACTIONS_CONTAINER)); |
|
| 33 | + return parent.findElement(new BySeleniumId(INPUT_SUGGEST_BOX)); |
|
| 34 | + } |
|
| 35 | + |
|
| 36 | +} |
|
| ... | ... | \ No newline at end of file |
java/com.sap.sailing.selenium.test/src/com/sap/sailing/selenium/pages/adminconsole/event/EventConfigurationPanelPO.java
| ... | ... | @@ -3,12 +3,14 @@ package com.sap.sailing.selenium.pages.adminconsole.event; |
| 3 | 3 | import java.util.Date; |
| 4 | 4 | |
| 5 | 5 | import org.openqa.selenium.By; |
| 6 | +import org.openqa.selenium.By.ByName; |
|
| 6 | 7 | import org.openqa.selenium.WebDriver; |
| 7 | 8 | import org.openqa.selenium.WebElement; |
| 8 | 9 | |
| 9 | 10 | import com.sap.sailing.selenium.core.BySeleniumId; |
| 10 | 11 | import com.sap.sailing.selenium.core.FindBy; |
| 11 | 12 | import com.sap.sailing.selenium.pages.PageArea; |
| 13 | +import com.sap.sailing.selenium.pages.adminconsole.AclPopupPO; |
|
| 12 | 14 | import com.sap.sailing.selenium.pages.adminconsole.leaderboard.LeaderboardGroupCreateDialogPO; |
| 13 | 15 | import com.sap.sailing.selenium.pages.adminconsole.regatta.RegattaCreateDialogPO; |
| 14 | 16 | import com.sap.sailing.selenium.pages.common.ConfirmDialogPO; |
| ... | ... | @@ -30,6 +32,9 @@ public class EventConfigurationPanelPO extends PageArea { |
| 30 | 32 | |
| 31 | 33 | public static class EventEntryPO extends DataEntryPO { |
| 32 | 34 | |
| 35 | + @FindBy(how = ByName.class, using = "CHANGE_ACL") |
|
| 36 | + private WebElement aclButton; |
|
| 37 | + |
|
| 33 | 38 | public EventEntryPO(CellTablePO<?> table, WebElement element) { |
| 34 | 39 | super(table, element); |
| 35 | 40 | } |
| ... | ... | @@ -41,7 +46,14 @@ public class EventConfigurationPanelPO extends PageArea { |
| 41 | 46 | |
| 42 | 47 | public String getEventURL() { |
| 43 | 48 | return getWebElement().findElement(By.xpath(".//td/div/a")).getAttribute("href"); |
| 44 | - } |
|
| 49 | + } |
|
| 50 | + |
|
| 51 | + public AclPopupPO openAclPopup() { |
|
| 52 | + aclButton.click(); |
|
| 53 | + waitForElement("AclDialog"); |
|
| 54 | + return new AclPopupPO(this.driver, driver.findElement(new BySeleniumId(("AclDialog")))); |
|
| 55 | + } |
|
| 56 | + |
|
| 45 | 57 | } |
| 46 | 58 | |
| 47 | 59 | @FindBy(how = BySeleniumId.class, using = "RefreshEventsButton") |
java/com.sap.sailing.server.gateway.serialization/META-INF/MANIFEST.MF
| ... | ... | @@ -15,7 +15,8 @@ Require-Bundle: com.sap.sailing.domain.common, |
| 15 | 15 | com.sap.sailing.server.gateway.serialization.shared.android, |
| 16 | 16 | com.sap.sse.security, |
| 17 | 17 | com.sap.sse.security.common, |
| 18 | - com.sap.sse.replication |
|
| 18 | + com.sap.sse.replication, |
|
| 19 | + com.sap.sailing.shared.server |
|
| 19 | 20 | Export-Package: com.sap.sailing.server.gateway.deserialization.impl, |
| 20 | 21 | com.sap.sailing.server.gateway.serialization, |
| 21 | 22 | com.sap.sailing.server.gateway.serialization.impl, |
java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/deserialization/impl/CourseConfigurationBuilder.java
| ... | ... | @@ -52,9 +52,9 @@ import com.sap.sailing.domain.coursetemplate.impl.MarkTemplateBasedMarkConfigura |
| 52 | 52 | import com.sap.sailing.domain.coursetemplate.impl.RegattaMarkConfigurationImpl; |
| 53 | 53 | import com.sap.sailing.domain.coursetemplate.impl.WaypointWithMarkConfigurationImpl; |
| 54 | 54 | import com.sap.sailing.domain.racelogtracking.DeviceMappingWithRegattaLogEvent; |
| 55 | -import com.sap.sailing.domain.sharedsailingdata.SharedSailingData; |
|
| 56 | 55 | import com.sap.sailing.domain.tracking.GPSFixTrack; |
| 57 | 56 | import com.sap.sailing.domain.tracking.TrackedRace; |
| 57 | +import com.sap.sailing.shared.server.SharedSailingData; |
|
| 58 | 58 | import com.sap.sse.common.TimeRange; |
| 59 | 59 | import com.sap.sse.common.Util.Triple; |
| 60 | 60 | import com.sap.sse.common.WithID; |
java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/deserialization/impl/CourseConfigurationJsonDeserializer.java
| ... | ... | @@ -22,10 +22,10 @@ import com.sap.sailing.domain.coursetemplate.MarkConfigurationRequestAnnotation. |
| 22 | 22 | import com.sap.sailing.domain.coursetemplate.Positioning; |
| 23 | 23 | import com.sap.sailing.domain.coursetemplate.RepeatablePart; |
| 24 | 24 | import com.sap.sailing.domain.coursetemplate.impl.MarkConfigurationRequestAnnotationImpl.MarkRoleCreationRequestImpl; |
| 25 | -import com.sap.sailing.domain.sharedsailingdata.SharedSailingData; |
|
| 26 | 25 | import com.sap.sailing.server.gateway.deserialization.JsonDeserializationException; |
| 27 | 26 | import com.sap.sailing.server.gateway.deserialization.JsonDeserializer; |
| 28 | 27 | import com.sap.sailing.server.gateway.serialization.impl.CourseConfigurationJsonSerializer; |
| 28 | +import com.sap.sailing.shared.server.SharedSailingData; |
|
| 29 | 29 | |
| 30 | 30 | public class CourseConfigurationJsonDeserializer implements JsonDeserializer<CourseConfiguration<MarkConfigurationRequestAnnotation>> { |
| 31 | 31 |
java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/AbstractSailingServerResource.java
| ... | ... | @@ -20,10 +20,10 @@ import com.sap.sailing.domain.base.Regatta; |
| 20 | 20 | import com.sap.sailing.domain.common.ScoreCorrectionProvider; |
| 21 | 21 | import com.sap.sailing.domain.racelogtracking.RaceLogTrackingAdapter; |
| 22 | 22 | import com.sap.sailing.domain.racelogtracking.RaceLogTrackingAdapterFactory; |
| 23 | -import com.sap.sailing.domain.sharedsailingdata.SharedSailingData; |
|
| 24 | 23 | import com.sap.sailing.domain.tracking.DynamicTrackedRegatta; |
| 25 | 24 | import com.sap.sailing.domain.tracking.TrackedRace; |
| 26 | 25 | import com.sap.sailing.server.interfaces.RacingEventService; |
| 26 | +import com.sap.sailing.shared.server.SharedSailingData; |
|
| 27 | 27 | import com.sap.sse.InvalidDateException; |
| 28 | 28 | import com.sap.sse.common.TimePoint; |
| 29 | 29 | import com.sap.sse.common.TypeBasedServiceFinderFactory; |
java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/RestServletContainer.java
| ... | ... | @@ -7,8 +7,8 @@ import javax.ws.rs.core.Application; |
| 7 | 7 | import org.osgi.framework.BundleContext; |
| 8 | 8 | import org.osgi.util.tracker.ServiceTracker; |
| 9 | 9 | |
| 10 | -import com.sap.sailing.domain.sharedsailingdata.SharedSailingData; |
|
| 11 | 10 | import com.sap.sailing.server.interfaces.RacingEventService; |
| 11 | +import com.sap.sailing.shared.server.SharedSailingData; |
|
| 12 | 12 | import com.sap.sse.datamining.DataMiningServer; |
| 13 | 13 | import com.sap.sse.replication.ReplicationService; |
| 14 | 14 | import com.sap.sse.security.SecurityService; |
java/com.sap.sailing.server.replication.test/META-INF/MANIFEST.MF
| ... | ... | @@ -37,5 +37,6 @@ Require-Bundle: com.sap.sailing.server, |
| 37 | 37 | com.sun.jersey;bundle-version="1.17.0", |
| 38 | 38 | com.sap.sse.security.testsupport, |
| 39 | 39 | com.sap.sailing.domain.racelogtrackingadapter, |
| 40 | - com.sap.sailing.resultimport;bundle-version="1.0.0" |
|
| 40 | + com.sap.sailing.shared.server, |
|
| 41 | + com.sap.sailing.resultimport |
|
| 41 | 42 | Automatic-Module-Name: com.sap.sailing.server.replication.test |
java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/AbstractServerReplicationTest.java
| ... | ... | @@ -55,8 +55,10 @@ public abstract class AbstractServerReplicationTest extends com.sap.sse.replicat |
| 55 | 55 | @Override public DomainFactory getBaseDomainFactory() { return baseDomainFactory; } |
| 56 | 56 | @Override public CompetitorAndBoatStore getCompetitorAndBoatStore() { return getBaseDomainFactory().getCompetitorAndBoatStore(); } |
| 57 | 57 | }; |
| 58 | - }, MediaDBFactory.INSTANCE.getMediaDB(mongoDBService), EmptyWindStore.INSTANCE, EmptySensorFixStore.INSTANCE, null, null, /* sailingNotificationService */ null, |
|
| 59 | - /* trackedRaceStatisticsCache */ null, /* restoreTrackedRaces */ false, null, /* sharedSailingData */ null, |
|
| 58 | + }, MediaDBFactory.INSTANCE.getMediaDB(mongoDBService), EmptyWindStore.INSTANCE, |
|
| 59 | + EmptySensorFixStore.INSTANCE, null, null, /* sailingNotificationService */ null, |
|
| 60 | + /* trackedRaceStatisticsCache */ null, /* restoreTrackedRaces */ false, |
|
| 61 | + /* security service tracker */ null, /* sharedSailingData */ null, /* replicationServiceTracker */ null, |
|
| 60 | 62 | /* scoreCorrectionProviderServiceTracker */ null, /* resultUrlRegistryServiceTracker */ null); |
| 61 | 63 | } |
| 62 | 64 | |
| ... | ... | @@ -75,9 +77,10 @@ public abstract class AbstractServerReplicationTest extends com.sap.sse.replicat |
| 75 | 77 | @Override public DomainFactory getBaseDomainFactory() { return domainObjectFactory.getBaseDomainFactory(); } |
| 76 | 78 | @Override public CompetitorAndBoatStore getCompetitorAndBoatStore() { return getBaseDomainFactory().getCompetitorAndBoatStore(); } |
| 77 | 79 | }; |
| 78 | - }, MediaDBFactory.INSTANCE.getMediaDB(mongoDBService), EmptyWindStore.INSTANCE, EmptySensorFixStore.INSTANCE, |
|
| 79 | - /* serviceFinderFactory */ null, null, /* sailingNotificationService */ null, |
|
| 80 | - /* trackedRaceStatisticsCache */ null, /* restoreTrackedRaces */ false, null, /* sharedSailingData */ null, |
|
| 80 | + }, MediaDBFactory.INSTANCE.getMediaDB(mongoDBService), EmptyWindStore.INSTANCE, |
|
| 81 | + EmptySensorFixStore.INSTANCE, /* serviceFinderFactory */ null, null, |
|
| 82 | + /* sailingNotificationService */ null, /* trackedRaceStatisticsCache */ null, |
|
| 83 | + /* restoreTrackedRaces */ false, /* security service tracker */ null, /* sharedSailingData */ null, /* replicationServiceTracker */ null, |
|
| 81 | 84 | /* scoreCorrectionProviderServiceTracker */ null, /* resultUrlRegistryServiceTracker */ null); |
| 82 | 85 | } |
| 83 | 86 | } |
java/com.sap.sailing.server/META-INF/MANIFEST.MF
| ... | ... | @@ -42,6 +42,7 @@ Require-Bundle: com.sap.sailing.domain, |
| 42 | 42 | com.sap.sailing.server.interface, |
| 43 | 43 | org.mongodb.mongo-java-driver;bundle-version="3.6.4", |
| 44 | 44 | com.sap.sse.security.common, |
| 45 | + com.sap.sailing.shared.server, |
|
| 45 | 46 | com.sap.sailing.resultimport |
| 46 | 47 | Bundle-ClassPath: . |
| 47 | 48 | Export-Package: com.sap.sailing.server, |
java/com.sap.sailing.server/src/com/sap/sailing/server/hierarchy/SailingHierarchyOwnershipUpdater.java
| ... | ... | @@ -274,14 +274,13 @@ public class SailingHierarchyOwnershipUpdater { |
| 274 | 274 | private static UserGroup copyUserGroup(UserGroup userGroupToCopy, String name, SecurityService securitySerice, |
| 275 | 275 | RacingEventService service) throws UserGroupManagementException { |
| 276 | 276 | // explicitly loading the current version of the group in case the given instance e.g. originates from the UI |
| 277 | - // and is possible out of date. |
|
| 277 | + // and is possibly out of date. |
|
| 278 | 278 | final UUID newGroupId = UUID.randomUUID(); |
| 279 | 279 | return securitySerice.setOwnershipCheckPermissionForObjectCreationAndRevertOnError( |
| 280 | 280 | SecuredSecurityTypes.USER_GROUP, UserGroupImpl.getTypeRelativeObjectIdentifier(newGroupId), name, () -> { |
| 281 | 281 | final UserGroup createdUserGroup = securitySerice.createUserGroup(newGroupId, name); |
| 282 | 282 | securitySerice.copyUsersAndRoleAssociations(userGroupToCopy, createdUserGroup, |
| 283 | 283 | new RoleCopyListener() { |
| 284 | - |
|
| 285 | 284 | @Override |
| 286 | 285 | public void onRoleCopy(User user, Role existingRole, Role copyRole) { |
| 287 | 286 | TypeRelativeObjectIdentifier existingAssociationTypeIdentifier = PermissionAndRoleAssociation |
java/com.sap.sailing.server/src/com/sap/sailing/server/impl/Activator.java
| ... | ... | @@ -41,7 +41,6 @@ import com.sap.sailing.domain.persistence.racelog.tracking.impl.GPSFixMongoHandl |
| 41 | 41 | import com.sap.sailing.domain.persistence.racelog.tracking.impl.GPSFixMovingMongoHandlerImpl; |
| 42 | 42 | import com.sap.sailing.domain.polars.PolarDataService; |
| 43 | 43 | import com.sap.sailing.domain.racelog.tracking.SensorFixStoreSupplier; |
| 44 | -import com.sap.sailing.domain.sharedsailingdata.SharedSailingData; |
|
| 45 | 44 | import com.sap.sailing.domain.tracking.TrackedRegattaListener; |
| 46 | 45 | import com.sap.sailing.domain.windestimation.WindEstimationFactoryService; |
| 47 | 46 | import com.sap.sailing.resultimport.ResultUrlRegistry; |
| ... | ... | @@ -56,6 +55,7 @@ import com.sap.sailing.server.notification.impl.SailingNotificationServiceImpl; |
| 56 | 55 | import com.sap.sailing.server.security.SailingViewerRole; |
| 57 | 56 | import com.sap.sailing.server.statistics.TrackedRaceStatisticsCache; |
| 58 | 57 | import com.sap.sailing.server.statistics.TrackedRaceStatisticsCacheImpl; |
| 58 | +import com.sap.sailing.shared.server.SharedSailingData; |
|
| 59 | 59 | import com.sap.sse.MasterDataImportClassLoaderService; |
| 60 | 60 | import com.sap.sse.common.TypeBasedServiceFinder; |
| 61 | 61 | import com.sap.sse.common.Util; |
| ... | ... | @@ -63,7 +63,9 @@ import com.sap.sse.mail.MailService; |
| 63 | 63 | import com.sap.sse.mail.queue.MailQueue; |
| 64 | 64 | import com.sap.sse.mail.queue.impl.ExecutorMailQueue; |
| 65 | 65 | import com.sap.sse.osgi.CachedOsgiTypeBasedServiceFinderFactory; |
| 66 | +import com.sap.sse.replication.FullyInitializedReplicableTracker; |
|
| 66 | 67 | import com.sap.sse.replication.Replicable; |
| 68 | +import com.sap.sse.replication.ReplicationService; |
|
| 67 | 69 | import com.sap.sse.security.SecurityInitializationCustomizer; |
| 68 | 70 | import com.sap.sse.security.SecurityService; |
| 69 | 71 | import com.sap.sse.security.interfaces.PreferenceConverter; |
| ... | ... | @@ -111,9 +113,11 @@ public class Activator implements BundleActivator { |
| 111 | 113 | |
| 112 | 114 | private ServiceTracker<MailService, MailService> mailServiceTracker; |
| 113 | 115 | |
| 114 | - private ServiceTracker<SecurityService, SecurityService> securityServiceTracker; |
|
| 116 | + private FullyInitializedReplicableTracker<SecurityService> securityServiceTracker; |
|
| 115 | 117 | |
| 116 | - private ServiceTracker<SharedSailingData, SharedSailingData> sharedSailingDataTracker; |
|
| 118 | + private FullyInitializedReplicableTracker<SharedSailingData> sharedSailingDataTracker; |
|
| 119 | + |
|
| 120 | + private ServiceTracker<ReplicationService, ReplicationService> replicationServiceTracker; |
|
| 117 | 121 | |
| 118 | 122 | public Activator() { |
| 119 | 123 | clearPersistentCompetitors = Boolean |
| ... | ... | @@ -130,13 +134,19 @@ public class Activator implements BundleActivator { |
| 130 | 134 | extenderBundleTracker = new ExtenderBundleTracker(context); |
| 131 | 135 | extenderBundleTracker.open(); |
| 132 | 136 | mailServiceTracker = ServiceTrackerFactory.createAndOpen(context, MailService.class); |
| 133 | - securityServiceTracker = ServiceTrackerFactory.createAndOpen(context, SecurityService.class); |
|
| 137 | + replicationServiceTracker = ServiceTrackerFactory.createAndOpen(context, ReplicationService.class); |
|
| 138 | + sharedSailingDataTracker = new FullyInitializedReplicableTracker<>(context, SharedSailingData.class, |
|
| 139 | + /* customizer */ null, replicationServiceTracker); |
|
| 140 | + sharedSailingDataTracker.open(); |
|
| 141 | + securityServiceTracker = new FullyInitializedReplicableTracker<>(context, SecurityService.class, |
|
| 142 | + /* customizer */ null, replicationServiceTracker); |
|
| 143 | + securityServiceTracker.open(); |
|
| 134 | 144 | if (securityServiceTracker != null) { |
| 135 | 145 | new Thread("Racingevent wait for securityservice for migration thread") { |
| 136 | 146 | public void run() { |
| 137 | 147 | try { |
| 138 | 148 | // only continue once we have the service, as some of the services require it to start properly |
| 139 | - securityServiceTracker.waitForService(0); |
|
| 149 | + securityServiceTracker.getInitializedService(0); |
|
| 140 | 150 | internalStartBundle(context); |
| 141 | 151 | } catch (Exception e) { |
| 142 | 152 | logger.log(Level.SEVERE, "Could not start RacingEvent service properly", e); |
| ... | ... | @@ -200,13 +210,15 @@ public class Activator implements BundleActivator { |
| 200 | 210 | mailQueue.stop(); |
| 201 | 211 | mailServiceTracker.close(); |
| 202 | 212 | sharedSailingDataTracker.close(); |
| 213 | + replicationServiceTracker.close(); |
|
| 203 | 214 | securityServiceTracker.close(); |
| 204 | 215 | MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); |
| 205 | 216 | mbs.unregisterMBean(mBeanName); |
| 206 | 217 | } |
| 207 | 218 | |
| 208 | 219 | private void internalStartBundle(BundleContext context) throws MalformedURLException, MalformedObjectNameException, |
| 209 | - InstanceAlreadyExistsException, MBeanRegistrationException, NotCompliantMBeanException { |
|
| 220 | + InstanceAlreadyExistsException, MBeanRegistrationException, NotCompliantMBeanException, InterruptedException { |
|
| 221 | + assert securityServiceTracker.getInitializedService(0) != null; // callers must call securityServiceTracker.waitForService(0) before calling this method |
|
| 210 | 222 | mailQueue = new ExecutorMailQueue(mailServiceTracker); |
| 211 | 223 | notificationService = new SailingNotificationServiceImpl(context, mailQueue); |
| 212 | 224 | trackedRegattaListener = new OSGiBasedTrackedRegattaListener(context); |
| ... | ... | @@ -236,7 +248,6 @@ public class Activator implements BundleActivator { |
| 236 | 248 | // this code block is not run, and the test case can inject some other type of finder |
| 237 | 249 | // instead. |
| 238 | 250 | serviceFinderFactory = new CachedOsgiTypeBasedServiceFinderFactory(context); |
| 239 | - sharedSailingDataTracker = ServiceTrackerFactory.createAndOpen(context, SharedSailingData.class); |
|
| 240 | 251 | ServiceTracker<ScoreCorrectionProvider, ScoreCorrectionProvider> scoreCorrectionProviderServiceTracker = |
| 241 | 252 | ServiceTrackerFactory.createAndOpen(context, ScoreCorrectionProvider.class); |
| 242 | 253 | ServiceTracker<ResultUrlRegistry, ResultUrlRegistry> resultUrlRegistryServiceTracker = ServiceTrackerFactory |
| ... | ... | @@ -244,7 +255,7 @@ public class Activator implements BundleActivator { |
| 244 | 255 | racingEventService = new RacingEventServiceImpl(clearPersistentCompetitors, |
| 245 | 256 | serviceFinderFactory, trackedRegattaListener, notificationService, |
| 246 | 257 | trackedRaceStatisticsCache, restoreTrackedRaces, securityServiceTracker, |
| 247 | - sharedSailingDataTracker, scoreCorrectionProviderServiceTracker, resultUrlRegistryServiceTracker); |
|
| 258 | + sharedSailingDataTracker, replicationServiceTracker, scoreCorrectionProviderServiceTracker, resultUrlRegistryServiceTracker); |
|
| 248 | 259 | notificationService.setRacingEventService(racingEventService); |
| 249 | 260 | masterDataImportClassLoaderServiceTracker = new ServiceTracker<MasterDataImportClassLoaderService, MasterDataImportClassLoaderService>( |
| 250 | 261 | context, MasterDataImportClassLoaderService.class, |
java/com.sap.sailing.server/src/com/sap/sailing/server/impl/CourseAndMarkConfigurationFactoryImpl.java
| ... | ... | @@ -18,8 +18,6 @@ import java.util.logging.Level; |
| 18 | 18 | import java.util.logging.Logger; |
| 19 | 19 | import java.util.stream.Collectors; |
| 20 | 20 | |
| 21 | -import org.osgi.util.tracker.ServiceTracker; |
|
| 22 | - |
|
| 23 | 21 | import com.sap.sailing.domain.abstractlog.AbstractLogEventAuthor; |
| 24 | 22 | import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceLogResolver; |
| 25 | 23 | import com.sap.sailing.domain.abstractlog.race.state.ReadonlyRaceState; |
| ... | ... | @@ -88,22 +86,23 @@ import com.sap.sailing.domain.racelog.tracking.SensorFixStore; |
| 88 | 86 | import com.sap.sailing.domain.racelogtracking.DeviceMappingWithRegattaLogEvent; |
| 89 | 87 | import com.sap.sailing.domain.racelogtracking.PingDeviceIdentifier; |
| 90 | 88 | import com.sap.sailing.domain.racelogtracking.impl.PingDeviceIdentifierImpl; |
| 91 | -import com.sap.sailing.domain.sharedsailingdata.SharedSailingData; |
|
| 92 | 89 | import com.sap.sailing.domain.tracking.TrackedRace; |
| 93 | 90 | import com.sap.sailing.server.gateway.deserialization.impl.CourseConfigurationBuilder; |
| 94 | 91 | import com.sap.sailing.server.interfaces.CourseAndMarkConfigurationFactory; |
| 92 | +import com.sap.sailing.shared.server.SharedSailingData; |
|
| 95 | 93 | import com.sap.sse.common.NoCorrespondingServiceRegisteredException; |
| 96 | 94 | import com.sap.sse.common.TimePoint; |
| 97 | 95 | import com.sap.sse.common.Timed; |
| 98 | 96 | import com.sap.sse.common.Util; |
| 99 | 97 | import com.sap.sse.common.Util.Pair; |
| 98 | +import com.sap.sse.replication.FullyInitializedReplicableTracker; |
|
| 100 | 99 | import com.sap.sse.security.shared.impl.UserGroup; |
| 101 | 100 | |
| 102 | 101 | public class CourseAndMarkConfigurationFactoryImpl implements CourseAndMarkConfigurationFactory { |
| 103 | 102 | |
| 104 | 103 | private static final Logger logger = Logger.getLogger(CourseAndMarkConfigurationFactoryImpl.class.getName()); |
| 105 | 104 | |
| 106 | - private final ServiceTracker<SharedSailingData, SharedSailingData> sharedSailingDataTracker; |
|
| 105 | + private final FullyInitializedReplicableTracker<SharedSailingData> sharedSailingDataTracker; |
|
| 107 | 106 | private final SensorFixStore sensorFixStore; |
| 108 | 107 | |
| 109 | 108 | /** |
| ... | ... | @@ -117,7 +116,7 @@ public class CourseAndMarkConfigurationFactoryImpl implements CourseAndMarkConfi |
| 117 | 116 | private final DomainFactory domainFactory; |
| 118 | 117 | |
| 119 | 118 | public CourseAndMarkConfigurationFactoryImpl( |
| 120 | - ServiceTracker<SharedSailingData, SharedSailingData> sharedSailingDataTracker, |
|
| 119 | + FullyInitializedReplicableTracker<SharedSailingData> sharedSailingDataTracker, |
|
| 121 | 120 | SensorFixStore sensorFixStore, RaceLogResolver raceLogResolver, DomainFactory domainFactory) { |
| 122 | 121 | this.sharedSailingDataTracker = sharedSailingDataTracker; |
| 123 | 122 | this.domainFactory = domainFactory; |
| ... | ... | @@ -139,7 +138,15 @@ public class CourseAndMarkConfigurationFactoryImpl implements CourseAndMarkConfi |
| 139 | 138 | } |
| 140 | 139 | |
| 141 | 140 | private SharedSailingData getSharedSailingData() { |
| 142 | - return sharedSailingDataTracker.getService(); |
|
| 141 | + SharedSailingData result; |
|
| 142 | + try { |
|
| 143 | + result = sharedSailingDataTracker.getInitializedService(0); |
|
| 144 | + } catch (InterruptedException e) { |
|
| 145 | + logger.log(Level.SEVERE, "Interrupted while waiting for a fully initialized SharedSailingData service; " |
|
| 146 | + + "continuing with null, probably causing a NullPointerException along the way", e); |
|
| 147 | + result = null; |
|
| 148 | + } |
|
| 149 | + return result; |
|
| 143 | 150 | } |
| 144 | 151 | |
| 145 | 152 | private CourseTemplate resolveCourseTemplateSafe(CourseBase course) { |
java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java
| ... | ... | @@ -189,7 +189,6 @@ import com.sap.sailing.domain.regattalike.IsRegattaLike; |
| 189 | 189 | import com.sap.sailing.domain.regattalike.LeaderboardThatHasRegattaLike; |
| 190 | 190 | import com.sap.sailing.domain.regattalog.RegattaLogStore; |
| 191 | 191 | import com.sap.sailing.domain.resultimport.ResultUrlProvider; |
| 192 | -import com.sap.sailing.domain.sharedsailingdata.SharedSailingData; |
|
| 193 | 192 | import com.sap.sailing.domain.statistics.Statistics; |
| 194 | 193 | import com.sap.sailing.domain.tracking.AddResult; |
| 195 | 194 | import com.sap.sailing.domain.tracking.DynamicRaceDefinitionSet; |
| ... | ... | @@ -209,13 +208,13 @@ import com.sap.sailing.domain.tracking.TrackedRace; |
| 209 | 208 | import com.sap.sailing.domain.tracking.TrackedRaceStatus; |
| 210 | 209 | import com.sap.sailing.domain.tracking.TrackedRegatta; |
| 211 | 210 | import com.sap.sailing.domain.tracking.TrackedRegattaListener; |
| 211 | +import com.sap.sailing.domain.tracking.TrackingConnectorInfo; |
|
| 212 | 212 | import com.sap.sailing.domain.tracking.WindStore; |
| 213 | 213 | import com.sap.sailing.domain.tracking.WindTracker; |
| 214 | 214 | import com.sap.sailing.domain.tracking.WindTrackerFactory; |
| 215 | 215 | import com.sap.sailing.domain.tracking.impl.AbstractRaceChangeListener; |
| 216 | 216 | import com.sap.sailing.domain.tracking.impl.DynamicTrackedRegattaImpl; |
| 217 | 217 | import com.sap.sailing.domain.tracking.impl.TrackedRaceImpl; |
| 218 | -import com.sap.sailing.domain.tracking.TrackingConnectorInfo; |
|
| 219 | 218 | import com.sap.sailing.domain.windestimation.WindEstimationFactoryService; |
| 220 | 219 | import com.sap.sailing.expeditionconnector.ExpeditionTrackerFactory; |
| 221 | 220 | import com.sap.sailing.resultimport.ResultUrlRegistry; |
| ... | ... | @@ -286,6 +285,7 @@ import com.sap.sailing.server.statistics.StatisticsCalculator; |
| 286 | 285 | import com.sap.sailing.server.statistics.TrackedRaceStatisticsCache; |
| 287 | 286 | import com.sap.sailing.server.tagging.TaggingServiceFactory; |
| 288 | 287 | import com.sap.sailing.server.util.EventUtil; |
| 288 | +import com.sap.sailing.shared.server.SharedSailingData; |
|
| 289 | 289 | import com.sap.sse.ServerInfo; |
| 290 | 290 | import com.sap.sse.common.Distance; |
| 291 | 291 | import com.sap.sse.common.Duration; |
| ... | ... | @@ -309,6 +309,7 @@ import com.sap.sse.pairinglist.PairingFrameProvider; |
| 309 | 309 | import com.sap.sse.pairinglist.PairingList; |
| 310 | 310 | import com.sap.sse.pairinglist.PairingListTemplate; |
| 311 | 311 | import com.sap.sse.pairinglist.PairingListTemplateFactory; |
| 312 | +import com.sap.sse.replication.FullyInitializedReplicableTracker; |
|
| 312 | 313 | import com.sap.sse.replication.OperationExecutionListener; |
| 313 | 314 | import com.sap.sse.replication.OperationWithResult; |
| 314 | 315 | import com.sap.sse.replication.OperationWithResultWithIdWrapper; |
| ... | ... | @@ -519,7 +520,7 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes |
| 519 | 520 | |
| 520 | 521 | private Set<OperationWithResultWithIdWrapper<?, ?>> operationsSentToMasterForReplication; |
| 521 | 522 | |
| 522 | - private boolean currentlyFillingFromInitialLoad = false; |
|
| 523 | + private volatile boolean currentlyFillingFromInitialLoad; |
|
| 523 | 524 | |
| 524 | 525 | private ThreadLocal<Boolean> currentlyApplyingOperationReceivedFromMaster = ThreadLocal.withInitial(() -> false); |
| 525 | 526 | |
| ... | ... | @@ -534,7 +535,7 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes |
| 534 | 535 | private long numberOfTrackedRacesToRestore; |
| 535 | 536 | |
| 536 | 537 | private final AtomicInteger numberOfTrackedRacesRestored; |
| 537 | - |
|
| 538 | + |
|
| 538 | 539 | private final ServiceTracker<ResultUrlRegistry, ResultUrlRegistry> resultUrlRegistryServiceTracker; |
| 539 | 540 | |
| 540 | 541 | private final ServiceTracker<ScoreCorrectionProvider, ScoreCorrectionProvider> scoreCorrectionProviderServiceTracker; |
| ... | ... | @@ -565,8 +566,8 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes |
| 565 | 566 | */ |
| 566 | 567 | private OperationsToMasterSendingQueue unsentOperationsToMasterSender; |
| 567 | 568 | |
| 568 | - private ServiceTracker<SecurityService, SecurityService> securityServiceTracker; |
|
| 569 | - |
|
| 569 | + private final FullyInitializedReplicableTracker<SecurityService> securityServiceTracker; |
|
| 570 | + |
|
| 570 | 571 | private final CourseAndMarkConfigurationFactory courseAndMarkConfigurationFactory; |
| 571 | 572 | |
| 572 | 573 | /** |
| ... | ... | @@ -609,7 +610,7 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes |
| 609 | 610 | |
| 610 | 611 | public RacingEventServiceImpl(boolean clearPersistentCompetitorAndBoatStore, final TypeBasedServiceFinderFactory serviceFinderFactory, boolean restoreTrackedRaces) { |
| 611 | 612 | this(clearPersistentCompetitorAndBoatStore, serviceFinderFactory, null, /* sailingNotificationService */ null, |
| 612 | - /* trackedRaceStatisticsCache */ null, restoreTrackedRaces, null, /* sharedSailingDataTracker */ null, |
|
| 613 | + /* trackedRaceStatisticsCache */ null, restoreTrackedRaces, null, /* sharedSailingDataTracker */ null, /* replicationServiceTracker */ null, |
|
| 613 | 614 | /* scoreCorrectionProviderServiceTracker */ null, /* resultUrlRegistryServiceTracker */ null); |
| 614 | 615 | } |
| 615 | 616 | |
| ... | ... | @@ -636,8 +637,8 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes |
| 636 | 637 | final TypeBasedServiceFinderFactory serviceFinderFactory, TrackedRegattaListenerManager trackedRegattaListener, |
| 637 | 638 | SailingNotificationService sailingNotificationService, |
| 638 | 639 | TrackedRaceStatisticsCache trackedRaceStatisticsCache, boolean restoreTrackedRaces, |
| 639 | - ServiceTracker<SecurityService, SecurityService> securityServiceTracker, |
|
| 640 | - ServiceTracker<SharedSailingData, SharedSailingData> sharedSailingDataTracker, |
|
| 640 | + FullyInitializedReplicableTracker<SecurityService> securityServiceTracker, |
|
| 641 | + FullyInitializedReplicableTracker<SharedSailingData> sharedSailingDataTracker, ServiceTracker<ReplicationService, ReplicationService> replicationServiceTracker, |
|
| 641 | 642 | ServiceTracker<ScoreCorrectionProvider, ScoreCorrectionProvider> scoreCorrectionProviderServiceTracker, |
| 642 | 643 | ServiceTracker<ResultUrlRegistry, ResultUrlRegistry> resultUrlRegistryServiceTracker) { |
| 643 | 644 | this((final RaceLogAndTrackedRaceResolver raceLogResolver) -> { |
| ... | ... | @@ -669,8 +670,9 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes |
| 669 | 670 | } |
| 670 | 671 | }; |
| 671 | 672 | }, MediaDBFactory.INSTANCE.getDefaultMediaDB(), null, null, serviceFinderFactory, trackedRegattaListener, |
| 672 | - sailingNotificationService, trackedRaceStatisticsCache, restoreTrackedRaces, securityServiceTracker, |
|
| 673 | - sharedSailingDataTracker, scoreCorrectionProviderServiceTracker, resultUrlRegistryServiceTracker); |
|
| 673 | + sailingNotificationService, trackedRaceStatisticsCache, restoreTrackedRaces, |
|
| 674 | + securityServiceTracker, sharedSailingDataTracker, /* replicationServiceTracker */ null, |
|
| 675 | + scoreCorrectionProviderServiceTracker, resultUrlRegistryServiceTracker); |
|
| 674 | 676 | } |
| 675 | 677 | |
| 676 | 678 | private RacingEventServiceImpl(final boolean clearPersistentCompetitorStore, WindStore windStore, |
| ... | ... | @@ -703,10 +705,11 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes |
| 703 | 705 | return competitorStore; |
| 704 | 706 | } |
| 705 | 707 | }; |
| 706 | - }, MediaDBFactory.INSTANCE.getDefaultMediaDB(), windStore, sensorFixStore, serviceFinderFactory, null, |
|
| 707 | - sailingNotificationService, /* trackedRaceStatisticsCache */ null, restoreTrackedRaces, null, |
|
| 708 | - /* sharedSailingDataTracker */ null, /* scoreCorrectionProviderServiceTracker */ null, |
|
| 709 | - /* resultUrlRegistryServiceTracker */ null); |
|
| 708 | + }, MediaDBFactory.INSTANCE.getDefaultMediaDB(), windStore, sensorFixStore, serviceFinderFactory, |
|
| 709 | + /* tracked regatta listener */ null, |
|
| 710 | + sailingNotificationService, /* trackedRaceStatisticsCache */ null, restoreTrackedRaces, |
|
| 711 | + /* security service tracker */ null, /* sharedSailingDataTracker */ null, /* replicationServiceTracker */ null, |
|
| 712 | + /* scoreCorrectionProviderServiceTracker */ null, /* resultUrlRegistryServiceTracker */ null); |
|
| 710 | 713 | } |
| 711 | 714 | |
| 712 | 715 | public RacingEventServiceImpl(final DomainObjectFactory domainObjectFactory, MongoObjectFactory mongoObjectFactory, |
| ... | ... | @@ -733,8 +736,10 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes |
| 733 | 736 | return getBaseDomainFactory().getCompetitorAndBoatStore(); |
| 734 | 737 | } |
| 735 | 738 | }; |
| 736 | - }, mediaDB, windStore, sensorFixStore, null, null, /* sailingNotificationService */ null, |
|
| 737 | - /* trackedRaceStatisticsCache */ null, restoreTrackedRaces, null, /* sharedSailingDataTracker */ null, |
|
| 739 | + }, mediaDB, windStore, sensorFixStore, /* service finder factory */ null, |
|
| 740 | + /* tracked regatta listener */ null, /* sailingNotificationService */ null, |
|
| 741 | + /* trackedRaceStatisticsCache */ null, restoreTrackedRaces, /* security service tracker */ null, |
|
| 742 | + /* sharedSailingDataTracker */ null, /* replicationServiceTracker */ null, |
|
| 738 | 743 | /* scoreCorrectionProviderServiceTracker */ null, /* resultUrlRegistryServiceTracker */ null); |
| 739 | 744 | } |
| 740 | 745 | |
| ... | ... | @@ -771,11 +776,13 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes |
| 771 | 776 | TypeBasedServiceFinderFactory serviceFinderFactory, TrackedRegattaListenerManager trackedRegattaListener, |
| 772 | 777 | SailingNotificationService sailingNotificationService, |
| 773 | 778 | TrackedRaceStatisticsCache trackedRaceStatisticsCache, boolean restoreTrackedRaces, |
| 774 | - ServiceTracker<SecurityService, SecurityService> securityServiceTracker, |
|
| 775 | - ServiceTracker<SharedSailingData, SharedSailingData> sharedSailingDataTracker, |
|
| 779 | + FullyInitializedReplicableTracker<SecurityService> securityServiceTracker, |
|
| 780 | + FullyInitializedReplicableTracker<SharedSailingData> sharedSailingDataTracker, |
|
| 781 | + ServiceTracker<ReplicationService, ReplicationService> replicationServiceTracker, |
|
| 776 | 782 | ServiceTracker<ScoreCorrectionProvider, ScoreCorrectionProvider> scoreCorrectionProviderServiceTracker, |
| 777 | 783 | ServiceTracker<ResultUrlRegistry, ResultUrlRegistry> resultUrlRegistryServiceTracker) { |
| 778 | 784 | logger.info("Created " + this); |
| 785 | + this.currentlyFillingFromInitialLoad = false; |
|
| 779 | 786 | this.securityServiceTracker = securityServiceTracker; |
| 780 | 787 | this.numberOfTrackedRacesRestored = new AtomicInteger(); |
| 781 | 788 | this.resultUrlRegistryServiceTracker = resultUrlRegistryServiceTracker; |
| ... | ... | @@ -4776,13 +4783,18 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes |
| 4776 | 4783 | return competitorWithBoat; |
| 4777 | 4784 | } |
| 4778 | 4785 | |
| 4779 | - @Override |
|
| 4780 | 4786 | /** |
| 4781 | 4787 | * This should only be used for replicable Operations that need access to the SecurityService, all other should |
| 4782 | 4788 | * obtain the SecurityService in another way. |
| 4783 | 4789 | */ |
| 4790 | + @Override |
|
| 4784 | 4791 | public SecurityService getSecurityService() { |
| 4785 | - return securityServiceTracker.getService(); |
|
| 4792 | + try { |
|
| 4793 | + return securityServiceTracker.getInitializedService(0); |
|
| 4794 | + } catch (InterruptedException e) { |
|
| 4795 | + logger.severe("Interrupted while waiting for security service; returning null"); |
|
| 4796 | + return null; |
|
| 4797 | + } |
|
| 4786 | 4798 | } |
| 4787 | 4799 | |
| 4788 | 4800 | @Override |
java/com.sap.sailing.server/src/com/sap/sailing/server/impl/ReplicatingSharedSailingData.java
| ... | ... | @@ -1,51 +0,0 @@ |
| 1 | -package com.sap.sailing.server.impl; |
|
| 2 | - |
|
| 3 | -import java.net.URL; |
|
| 4 | -import java.util.ArrayList; |
|
| 5 | -import java.util.Map; |
|
| 6 | -import java.util.UUID; |
|
| 7 | - |
|
| 8 | -import com.sap.sailing.domain.common.DeviceIdentifier; |
|
| 9 | -import com.sap.sailing.domain.common.Position; |
|
| 10 | -import com.sap.sailing.domain.coursetemplate.CommonMarkProperties; |
|
| 11 | -import com.sap.sailing.domain.coursetemplate.MarkRole; |
|
| 12 | -import com.sap.sailing.domain.coursetemplate.MarkTemplate; |
|
| 13 | -import com.sap.sailing.domain.coursetemplate.RepeatablePart; |
|
| 14 | -import com.sap.sailing.domain.coursetemplate.WaypointTemplate; |
|
| 15 | -import com.sap.sailing.domain.sharedsailingdata.SharedSailingData; |
|
| 16 | -import com.sap.sse.replication.OperationWithResult; |
|
| 17 | -import com.sap.sse.replication.ReplicableWithObjectInputStream; |
|
| 18 | - |
|
| 19 | -public interface ReplicatingSharedSailingData extends SharedSailingData, |
|
| 20 | - ReplicableWithObjectInputStream<ReplicatingSharedSailingData, OperationWithResult<ReplicatingSharedSailingData, ?>> { |
|
| 21 | - |
|
| 22 | - Void internalCreateMarkRole(UUID idOfNewMarkRole, String name); |
|
| 23 | - |
|
| 24 | - Void internalCreateMarkProperties(UUID idOfNewMarkProperties, CommonMarkProperties properties, |
|
| 25 | - Iterable<String> tags); |
|
| 26 | - |
|
| 27 | - Void internalUpdateMarkProperties(UUID idOfNewMarkProperties, CommonMarkProperties properties, Position position, |
|
| 28 | - DeviceIdentifier deviceIdentifier, Iterable<String> tags); |
|
| 29 | - |
|
| 30 | - Void internalCreateMarkTemplate(UUID idOfNewMarkTemplate, CommonMarkProperties properties); |
|
| 31 | - |
|
| 32 | - Void internalCreateCourseTemplate(UUID idOfNewCourseTemplate, String courseTemplateName, |
|
| 33 | - Iterable<MarkTemplate> marks, Iterable<WaypointTemplate> waypoints, |
|
| 34 | - Map<MarkTemplate, MarkRole> associatedRoles, RepeatablePart optionalRepeatablePart, Iterable<String> tags, |
|
| 35 | - URL optionalImageURL, Integer defaultNumberOfLaps); |
|
| 36 | - |
|
| 37 | - Void internalSetTrackingDeviceIdentifierForMarkProperties(UUID markPropertiesUUID, DeviceIdentifier deviceIdentifier); |
|
| 38 | - |
|
| 39 | - Void internalSetFixedPositionForMarkProperties(UUID markPropertiesUUID, Position position); |
|
| 40 | - |
|
| 41 | - Void internalDeleteMarkProperties(UUID markPropertiesUUID); |
|
| 42 | - |
|
| 43 | - Void internalDeleteCourseTemplate(UUID courseTemplateUUID); |
|
| 44 | - |
|
| 45 | - Void internalRecordUsage(UUID markTemplateId, UUID markPropertiesId); |
|
| 46 | - |
|
| 47 | - Void internalRecordUsage(UUID markPropertiesId, MarkRole roleName); |
|
| 48 | - |
|
| 49 | - Void internalUpdateCourseTemplate(UUID uuid, String name, URL optionalImageURL, ArrayList<String> tags); |
|
| 50 | - |
|
| 51 | -} |
java/com.sap.sailing.shared.server.gateway/src/com/sap/sailing/shared/server/gateway/jaxrs/AbstractSailingServerResource.java
| ... | ... | @@ -13,10 +13,10 @@ import com.sap.sailing.domain.base.Fleet; |
| 13 | 13 | import com.sap.sailing.domain.base.RaceColumn; |
| 14 | 14 | import com.sap.sailing.domain.base.RaceDefinition; |
| 15 | 15 | import com.sap.sailing.domain.base.Regatta; |
| 16 | -import com.sap.sailing.domain.sharedsailingdata.SharedSailingData; |
|
| 17 | 16 | import com.sap.sailing.domain.tracking.DynamicTrackedRegatta; |
| 18 | 17 | import com.sap.sailing.domain.tracking.TrackedRace; |
| 19 | 18 | import com.sap.sailing.server.interfaces.RacingEventService; |
| 19 | +import com.sap.sailing.shared.server.SharedSailingData; |
|
| 20 | 20 | import com.sap.sse.InvalidDateException; |
| 21 | 21 | import com.sap.sse.common.TimePoint; |
| 22 | 22 | import com.sap.sse.common.TypeBasedServiceFinderFactory; |
java/com.sap.sailing.shared.server.gateway/src/com/sap/sailing/shared/server/gateway/jaxrs/RestServletContainer.java
| ... | ... | @@ -7,8 +7,8 @@ import javax.ws.rs.core.Application; |
| 7 | 7 | import org.osgi.framework.BundleContext; |
| 8 | 8 | import org.osgi.util.tracker.ServiceTracker; |
| 9 | 9 | |
| 10 | -import com.sap.sailing.domain.sharedsailingdata.SharedSailingData; |
|
| 11 | 10 | import com.sap.sailing.server.interfaces.RacingEventService; |
| 11 | +import com.sap.sailing.shared.server.SharedSailingData; |
|
| 12 | 12 | import com.sap.sse.replication.ReplicationService; |
| 13 | 13 | import com.sap.sse.security.SecurityService; |
| 14 | 14 | import com.sun.jersey.spi.container.servlet.ServletContainer; |
java/com.sap.sailing.shared.server/META-INF/MANIFEST.MF
| ... | ... | @@ -7,38 +7,26 @@ Bundle-Version: 1.0.0.qualifier |
| 7 | 7 | Bundle-Activator: com.sap.sailing.shared.server.impl.Activator |
| 8 | 8 | Bundle-Vendor: SAP |
| 9 | 9 | Bundle-RequiredExecutionEnvironment: JavaSE-1.8 |
| 10 | -Import-Package: com.sap.sailing.server.gateway.deserialization, |
|
| 11 | - javax.mail;version="1.4.0", |
|
| 12 | - javax.mail.internet;version="1.4.0", |
|
| 13 | - javax.mail.util;version="1.4.0", |
|
| 14 | - org.json.simple, |
|
| 15 | - org.json.simple.parser, |
|
| 16 | - org.osgi.framework;version="1.3.0", |
|
| 10 | +Import-Package: org.osgi.framework;version="1.3.0", |
|
| 17 | 11 | org.osgi.util.tracker;version="1.3.1" |
| 18 | 12 | Bundle-ActivationPolicy: lazy |
| 19 | -Require-Bundle: com.sap.sailing.domain, |
|
| 20 | - com.sap.sse.mongodb, |
|
| 21 | - com.sap.sailing.domain.common, |
|
| 13 | +Require-Bundle: com.sap.sailing.domain.common, |
|
| 22 | 14 | com.sap.sailing.domain.shared.android, |
| 23 | - com.sap.sailing.server.gateway.serialization, |
|
| 24 | 15 | com.sap.sse.common, |
| 25 | - com.sap.sse.operationaltransformation, |
|
| 26 | 16 | com.sap.sse.replication.interfaces, |
| 27 | 17 | com.sap.sse, |
| 28 | - com.sap.sailing.server.gateway.serialization.shared.android, |
|
| 29 | 18 | com.sap.sse.shared.android, |
| 30 | - com.sap.sse.security, |
|
| 31 | 19 | com.sap.sse.security.common, |
| 32 | 20 | org.apache.shiro.web;bundle-version="1.2.2", |
| 33 | 21 | org.apache.shiro.core;bundle-version="1.2.2", |
| 34 | - javax.servlet;bundle-version="3.1.0", |
|
| 35 | - com.sap.sse.mail, |
|
| 36 | - com.sap.sailing.server.interface, |
|
| 37 | - org.mongodb.mongo-java-driver;bundle-version="3.6.4", |
|
| 38 | 22 | com.sap.sse.security.common, |
| 39 | - com.sap.sailing.shared.persistence |
|
| 23 | + com.sap.sse.operationaltransformation, |
|
| 24 | + com.sap.sse.security, |
|
| 25 | + com.sap.sailing.shared.persistence, |
|
| 26 | + javax.servlet;bundle-version="3.1.0" |
|
| 40 | 27 | Bundle-ClassPath: . |
| 41 | -Export-Package: com.sap.sailing.shared.server.impl; |
|
| 28 | +Export-Package: com.sap.sailing.shared.server, |
|
| 29 | + com.sap.sailing.shared.server.impl; |
|
| 42 | 30 | x-friends:="com.sap.sailing.mongodb.test, |
| 43 | 31 | com.sap.sailing.server.test, |
| 44 | 32 | com.sap.sailing.server.gateway.test, |
java/com.sap.sailing.shared.server/src/com/sap/sailing/shared/server/SharedSailingData.java
| ... | ... | @@ -0,0 +1,143 @@ |
| 1 | +package com.sap.sailing.shared.server; |
|
| 2 | + |
|
| 3 | +import java.net.URL; |
|
| 4 | +import java.util.ArrayList; |
|
| 5 | +import java.util.Map; |
|
| 6 | +import java.util.Optional; |
|
| 7 | +import java.util.UUID; |
|
| 8 | + |
|
| 9 | +import com.sap.sailing.domain.common.DeviceIdentifier; |
|
| 10 | +import com.sap.sailing.domain.common.Position; |
|
| 11 | +import com.sap.sailing.domain.coursetemplate.CommonMarkProperties; |
|
| 12 | +import com.sap.sailing.domain.coursetemplate.CourseTemplate; |
|
| 13 | +import com.sap.sailing.domain.coursetemplate.FixedPositioning; |
|
| 14 | +import com.sap.sailing.domain.coursetemplate.MarkProperties; |
|
| 15 | +import com.sap.sailing.domain.coursetemplate.MarkRole; |
|
| 16 | +import com.sap.sailing.domain.coursetemplate.MarkTemplate; |
|
| 17 | +import com.sap.sailing.domain.coursetemplate.Positioning; |
|
| 18 | +import com.sap.sailing.domain.coursetemplate.RepeatablePart; |
|
| 19 | +import com.sap.sailing.domain.coursetemplate.WaypointTemplate; |
|
| 20 | +import com.sap.sailing.shared.server.impl.ReplicatingSharedSailingData; |
|
| 21 | +import com.sap.sse.common.TimePoint; |
|
| 22 | +import com.sap.sse.common.impl.MillisecondsTimePoint; |
|
| 23 | +import com.sap.sse.replication.OperationWithResult; |
|
| 24 | +import com.sap.sse.replication.ReplicableWithObjectInputStream; |
|
| 25 | +import com.sap.sse.security.SecurityService; |
|
| 26 | +import com.sap.sse.security.shared.HasPermissions.DefaultActions; |
|
| 27 | +import com.sap.sse.security.shared.impl.UserGroup; |
|
| 28 | + |
|
| 29 | +/** |
|
| 30 | + * {@link com.sap.sailing.server.interfaces.RacingEventService RacingEventService} is scoped to one server only. To |
|
| 31 | + * share data that is not exclusively bound to one specific server but relevant for several servers, |
|
| 32 | + * {@link SharedSailingData} provides an alternative replication scope. This means {@link SharedSailingData} may be |
|
| 33 | + * replicated in a similar way as {@link SecurityService}.<br> |
|
| 34 | + * {@link SharedSailingData} encapsulates persistence, replication and security aspects for the following domain types: |
|
| 35 | + * <ul> |
|
| 36 | + * <li>{@link MarkTemplate}s</li> |
|
| 37 | + * <li>{@link MarkProperties}</li> |
|
| 38 | + * <li>{@link CourseTemplate}s</li> |
|
| 39 | + * </ul> |
|
| 40 | + * |
|
| 41 | + * In particular, the reading methods that produce iterables of entities, such as {@link #getAllMarkRoles()} or |
|
| 42 | + * {@link #getAllMarkTemplates()}, will deliver a view restricted by the availability of the {@link DefaultActions#READ} |
|
| 43 | + * permission of the user on whose behalf the request is being executed. |
|
| 44 | + * |
|
| 45 | + * @author Axel Uhl (D043530) |
|
| 46 | + * |
|
| 47 | + */ |
|
| 48 | +public interface SharedSailingData extends ReplicableWithObjectInputStream<ReplicatingSharedSailingData, OperationWithResult<ReplicatingSharedSailingData, ?>> { |
|
| 49 | + |
|
| 50 | + Iterable<MarkProperties> getAllMarkProperties(Iterable<String> tagsToFilterFor); |
|
| 51 | + |
|
| 52 | + Iterable<MarkTemplate> getAllMarkTemplates(); |
|
| 53 | + |
|
| 54 | + Iterable<CourseTemplate> getAllCourseTemplates(Iterable<String> tagsToFilterFor); |
|
| 55 | + |
|
| 56 | + Iterable<MarkRole> getAllMarkRoles(); |
|
| 57 | + |
|
| 58 | + MarkRole createMarkRole(String name, String shortName); |
|
| 59 | + |
|
| 60 | + MarkRole getMarkRoleById(UUID id); |
|
| 61 | + |
|
| 62 | + /** |
|
| 63 | + * @param optionalNonDefaultGroupOwnership |
|
| 64 | + * if {@link Optional#isPresent() present}, defines the {@link UserGroup} to use for the new mark |
|
| 65 | + * properties object's group ownership. Otherwise, the session user's default creation group ownership |
|
| 66 | + * will be used for the current server. |
|
| 67 | + */ |
|
| 68 | + MarkProperties createMarkProperties(CommonMarkProperties properties, Iterable<String> tags, Optional<UserGroup> optionalNonDefaultGroupOwnership); |
|
| 69 | + |
|
| 70 | + MarkProperties updateMarkProperties(UUID uuid, CommonMarkProperties properties, Iterable<String> tags); |
|
| 71 | + |
|
| 72 | + /** |
|
| 73 | + * @param positioningInformation |
|
| 74 | + * if {@code null}, no update will be performed to the positioning information of the |
|
| 75 | + * {@link MarkProperties} object identified by {@code uuid}. To clear the positioning information, pass |
|
| 76 | + * in a valid, non-{@code null} {@link Positioning} object that makes an "empty" specification, such as a |
|
| 77 | + * {@link FixedPositioning} with a {@code null} {@link FixedPositioning#getFixedPosition()} return. |
|
| 78 | + */ |
|
| 79 | + MarkProperties updateMarkProperties(UUID uuid, CommonMarkProperties properties, Positioning positioningInformation, Iterable<String> tags); |
|
| 80 | + |
|
| 81 | + /** |
|
| 82 | + * This overrides a previously set fixed position or associated tracking device. |
|
| 83 | + */ |
|
| 84 | + void setFixedPositionForMarkProperties(MarkProperties markProperties, Position position); |
|
| 85 | + |
|
| 86 | + MarkProperties getMarkPropertiesById(UUID id); |
|
| 87 | + |
|
| 88 | + /** |
|
| 89 | + * This overrides a previously set fixed position or associated tracking device. |
|
| 90 | + */ |
|
| 91 | + void setTrackingDeviceIdentifierForMarkProperties(MarkProperties markProperties, DeviceIdentifier deviceIdentifier); |
|
| 92 | + |
|
| 93 | + MarkTemplate createMarkTemplate(CommonMarkProperties properties); |
|
| 94 | + |
|
| 95 | + MarkTemplate getMarkTemplateById(UUID id); |
|
| 96 | + |
|
| 97 | + /** |
|
| 98 | + * @param waypoints the waypoints in their defined order (iteration order equals order of waypoints in course) |
|
| 99 | + */ |
|
| 100 | + CourseTemplate createCourseTemplate(String courseTemplateName, String courseTemplateShortName, |
|
| 101 | + Iterable<MarkTemplate> marks, Iterable<WaypointTemplate> waypoints, |
|
| 102 | + Map<MarkTemplate, MarkRole> associatedRoles, Map<MarkRole, MarkTemplate> defaultMarkTemplatesForMarkRoles, |
|
| 103 | + RepeatablePart optionalRepeatablePart, Iterable<String> tags, URL optionalImageURL, Integer defaultNumberOfLaps); |
|
| 104 | + |
|
| 105 | + CourseTemplate getCourseTemplateById(UUID id); |
|
| 106 | + |
|
| 107 | + /** |
|
| 108 | + * Records the fact that the {@code markProperties} were used to configure a mark based on a |
|
| 109 | + * {@code markTemplate}. Keeps the {@link MillisecondsTimePoint#now() current time} of this call which will be |
|
| 110 | + * returned for {@code markTemplate} when invoking {@link #getUsedMarkProperties(MarkTemplate)}. |
|
| 111 | + */ |
|
| 112 | + void recordUsage(MarkTemplate markTemplate, MarkProperties markProperties); |
|
| 113 | + |
|
| 114 | + /** |
|
| 115 | + * Records the fact that the {@code markProperties} were used to configure a mark that takes the given role name. |
|
| 116 | + * Keeps the {@link MillisecondsTimePoint#now() current time} of this call which will be returned for the role name |
|
| 117 | + * when invoking {@link #getUsedMarkProperties(String)}. |
|
| 118 | + */ |
|
| 119 | + void recordUsage(MarkProperties markProperties, MarkRole markRole); |
|
| 120 | + |
|
| 121 | + /** |
|
| 122 | + * Returns the time points when {@link MarkProperties} objects were {@link #recordUsage(MarkTemplate, MarkProperties) last used} |
|
| 123 | + * for the {@link MarkTemplate} passed in the {@code markTemplate} parameter. |
|
| 124 | + */ |
|
| 125 | + Map<MarkProperties, TimePoint> getUsedMarkProperties(MarkTemplate markTemplate); |
|
| 126 | + |
|
| 127 | + /** |
|
| 128 | + * Returns the time points when {@link MarkProperties} objects were {@link #recordUsage(MarkProperties, String) last used} |
|
| 129 | + * for the role name passed in the {@code roleName} parameter. |
|
| 130 | + */ |
|
| 131 | + Map<MarkProperties, TimePoint> getUsedMarkProperties(MarkRole roleName); |
|
| 132 | + |
|
| 133 | + void deleteMarkProperties(MarkProperties markProperties); |
|
| 134 | + |
|
| 135 | + void deleteCourseTemplate(CourseTemplate courseTemplate); |
|
| 136 | + |
|
| 137 | + Iterable<MarkProperties> getAllMarkProperties(); |
|
| 138 | + |
|
| 139 | + Iterable<CourseTemplate> getAllCourseTemplates(); |
|
| 140 | + |
|
| 141 | + CourseTemplate updateCourseTemplate(UUID uuid, String name, String shortName, URL optionalImageURL, ArrayList<String> tags, |
|
| 142 | + Integer defaultNumberOfLaps); |
|
| 143 | +} |
java/com.sap.sailing.shared.server/src/com/sap/sailing/shared/server/impl/Activator.java
| 0 | 144 | index 9d3c0a3..06aa671 |
| 1 | --- a/java/com.sap.sailing.shared.server/src/com/sap/sailing/shared/server/impl/Activator.java |
|
| 145 | +++ b/java/com.sap.sailing.shared.server/src/com/sap/sailing/shared/server/impl/Activator.java |
|
| ... | ... | @@ -1,17 +1,20 @@ |
| 1 | 1 | package com.sap.sailing.shared.server.impl; |
| 2 | 2 | |
| 3 | +import java.util.Dictionary; |
|
| 3 | 4 | import java.util.HashSet; |
| 5 | +import java.util.Hashtable; |
|
| 4 | 6 | import java.util.Set; |
| 5 | 7 | |
| 6 | 8 | import org.osgi.framework.BundleActivator; |
| 7 | 9 | import org.osgi.framework.BundleContext; |
| 8 | 10 | import org.osgi.framework.ServiceRegistration; |
| 9 | -import org.osgi.util.tracker.ServiceTracker; |
|
| 10 | 11 | |
| 11 | -import com.sap.sailing.domain.sharedsailingdata.SharedSailingData; |
|
| 12 | 12 | import com.sap.sailing.shared.persistence.PersistenceFactory; |
| 13 | +import com.sap.sailing.shared.server.SharedSailingData; |
|
| 13 | 14 | import com.sap.sse.osgi.CachedOsgiTypeBasedServiceFinderFactory; |
| 15 | +import com.sap.sse.replication.FullyInitializedReplicableTracker; |
|
| 14 | 16 | import com.sap.sse.replication.Replicable; |
| 17 | +import com.sap.sse.replication.ReplicationService; |
|
| 15 | 18 | import com.sap.sse.security.SecurityService; |
| 16 | 19 | import com.sap.sse.util.ClearStateTestSupport; |
| 17 | 20 | import com.sap.sse.util.ServiceTrackerFactory; |
| ... | ... | @@ -24,21 +27,27 @@ public class Activator implements BundleActivator { |
| 24 | 27 | |
| 25 | 28 | private Set<ServiceRegistration<?>> registrations = new HashSet<>(); |
| 26 | 29 | |
| 27 | - private ServiceTracker<SecurityService, SecurityService> securityServiceTracker; |
|
| 30 | + private FullyInitializedReplicableTracker<SecurityService> securityServiceTracker; |
|
| 28 | 31 | |
| 29 | 32 | private SharedSailingDataImpl sharedSailingData; |
| 30 | 33 | |
| 31 | 34 | public void start(BundleContext context) throws Exception { |
| 32 | 35 | Activator.context = context; |
| 33 | - securityServiceTracker = ServiceTrackerFactory.createAndOpen(context, SecurityService.class); |
|
| 36 | + securityServiceTracker = new FullyInitializedReplicableTracker<>(context, SecurityService.class, |
|
| 37 | + /* customizer */ null, ServiceTrackerFactory.createAndOpen(context, ReplicationService.class)); |
|
| 38 | + securityServiceTracker.open(); |
|
| 34 | 39 | serviceFinderFactory = new CachedOsgiTypeBasedServiceFinderFactory(context); |
| 35 | 40 | sharedSailingData = new SharedSailingDataImpl(PersistenceFactory.INSTANCE.getDefaultDomainObjectFactory(serviceFinderFactory), |
| 36 | 41 | PersistenceFactory.INSTANCE.getDefaultMongoObjectFactory(serviceFinderFactory), serviceFinderFactory, |
| 37 | 42 | securityServiceTracker); |
| 38 | 43 | registrations.add(context.registerService(SharedSailingData.class, sharedSailingData, /* properties */ null)); |
| 44 | + registrations.add(context.registerService(ReplicatingSharedSailingData.class, sharedSailingData, /* properties */ null)); |
|
| 39 | 45 | registrations |
| 40 | 46 | .add(context.registerService(ClearStateTestSupport.class, sharedSailingData, /* properties */ null)); |
| 41 | - registrations.add(context.registerService(Replicable.class, sharedSailingData, /* properties */ null)); |
|
| 47 | + final Dictionary<String, String> replicableServiceProperties = new Hashtable<>(); |
|
| 48 | + replicableServiceProperties.put(Replicable.OSGi_Service_Registry_ID_Property_Name, |
|
| 49 | + sharedSailingData.getId().toString()); |
|
| 50 | + registrations.add(context.registerService(Replicable.class, sharedSailingData, /* properties */ replicableServiceProperties)); |
|
| 42 | 51 | } |
| 43 | 52 | |
| 44 | 53 | public static BundleContext getContext() { |
java/com.sap.sailing.shared.server/src/com/sap/sailing/shared/server/impl/ReplicatingSharedSailingData.java
| ... | ... | @@ -11,12 +11,9 @@ import com.sap.sailing.domain.coursetemplate.MarkTemplate; |
| 11 | 11 | import com.sap.sailing.domain.coursetemplate.Positioning; |
| 12 | 12 | import com.sap.sailing.domain.coursetemplate.RepeatablePart; |
| 13 | 13 | import com.sap.sailing.domain.coursetemplate.WaypointTemplate; |
| 14 | -import com.sap.sailing.domain.sharedsailingdata.SharedSailingData; |
|
| 15 | -import com.sap.sse.replication.OperationWithResult; |
|
| 16 | -import com.sap.sse.replication.ReplicableWithObjectInputStream; |
|
| 14 | +import com.sap.sailing.shared.server.SharedSailingData; |
|
| 17 | 15 | |
| 18 | -public interface ReplicatingSharedSailingData extends SharedSailingData, |
|
| 19 | - ReplicableWithObjectInputStream<ReplicatingSharedSailingData, OperationWithResult<ReplicatingSharedSailingData, ?>> { |
|
| 16 | +public interface ReplicatingSharedSailingData extends SharedSailingData { |
|
| 20 | 17 | |
| 21 | 18 | Void internalCreateMarkRole(UUID idOfNewMarkRole, String name, String shortName); |
| 22 | 19 |
java/com.sap.sailing.shared.server/src/com/sap/sailing/shared/server/impl/SharedSailingDataImpl.java
| ... | ... | @@ -17,8 +17,6 @@ import java.util.UUID; |
| 17 | 17 | import java.util.concurrent.ConcurrentHashMap; |
| 18 | 18 | import java.util.stream.Collectors; |
| 19 | 19 | |
| 20 | -import org.osgi.util.tracker.ServiceTracker; |
|
| 21 | - |
|
| 22 | 20 | import com.sap.sailing.domain.common.DeviceIdentifier; |
| 23 | 21 | import com.sap.sailing.domain.common.Position; |
| 24 | 22 | import com.sap.sailing.domain.common.security.SecuredDomainType; |
| ... | ... | @@ -47,6 +45,7 @@ import com.sap.sse.common.TypeBasedServiceFinder; |
| 47 | 45 | import com.sap.sse.common.TypeBasedServiceFinderFactory; |
| 48 | 46 | import com.sap.sse.common.Util; |
| 49 | 47 | import com.sap.sse.common.impl.MillisecondsTimePoint; |
| 48 | +import com.sap.sse.replication.FullyInitializedReplicableTracker; |
|
| 50 | 49 | import com.sap.sse.replication.OperationExecutionListener; |
| 51 | 50 | import com.sap.sse.replication.OperationWithResult; |
| 52 | 51 | import com.sap.sse.replication.OperationWithResultWithIdWrapper; |
| ... | ... | @@ -69,11 +68,11 @@ public class SharedSailingDataImpl implements ReplicatingSharedSailingData, Clea |
| 69 | 68 | private final Map<UUID, MarkRole> markRolesById = new ConcurrentHashMap<>(); |
| 70 | 69 | |
| 71 | 70 | private final TypeBasedServiceFinder<DeviceIdentifierMongoHandler> deviceIdentifierServiceFinder; |
| 72 | - private final ServiceTracker<SecurityService, SecurityService> securityServiceTracker; |
|
| 71 | + private final FullyInitializedReplicableTracker<SecurityService> securityServiceTracker; |
|
| 73 | 72 | |
| 74 | 73 | // Replication related methods and fields |
| 75 | 74 | private final ConcurrentHashMap<OperationExecutionListener<ReplicatingSharedSailingData>, OperationExecutionListener<ReplicatingSharedSailingData>> operationExecutionListeners = new ConcurrentHashMap<>(); |
| 76 | - private ThreadLocal<Boolean> currentlyFillingFromInitialLoad = ThreadLocal.withInitial(() -> false); |
|
| 75 | + private volatile boolean currentlyFillingFromInitialLoad; |
|
| 77 | 76 | private ThreadLocal<Boolean> currentlyApplyingOperationReceivedFromMaster = ThreadLocal.withInitial(() -> false); |
| 78 | 77 | private final Set<OperationWithResultWithIdWrapper<ReplicatingSharedSailingData, ?>> operationsSentToMasterForReplication = new HashSet<>(); |
| 79 | 78 | private ReplicationMasterDescriptor master; |
| ... | ... | @@ -87,7 +86,8 @@ public class SharedSailingDataImpl implements ReplicatingSharedSailingData, Clea |
| 87 | 86 | |
| 88 | 87 | public SharedSailingDataImpl(final DomainObjectFactory domainObjectFactory, |
| 89 | 88 | final MongoObjectFactory mongoObjectFactory, TypeBasedServiceFinderFactory serviceFinderFactory, |
| 90 | - ServiceTracker<SecurityService, SecurityService> securityServiceTracker) { |
|
| 89 | + FullyInitializedReplicableTracker<SecurityService> securityServiceTracker) { |
|
| 90 | + this.currentlyFillingFromInitialLoad = false; |
|
| 91 | 91 | this.domainObjectFactory = domainObjectFactory; |
| 92 | 92 | this.mongoObjectFactory = mongoObjectFactory; |
| 93 | 93 | this.deviceIdentifierServiceFinder = serviceFinderFactory |
| ... | ... | @@ -123,7 +123,11 @@ public class SharedSailingDataImpl implements ReplicatingSharedSailingData, Clea |
| 123 | 123 | } |
| 124 | 124 | |
| 125 | 125 | public SecurityService getSecurityService() { |
| 126 | - return securityServiceTracker.getService(); |
|
| 126 | + try { |
|
| 127 | + return securityServiceTracker.getInitializedService(0); |
|
| 128 | + } catch (InterruptedException e) { |
|
| 129 | + throw new RuntimeException(e); |
|
| 130 | + } |
|
| 127 | 131 | } |
| 128 | 132 | |
| 129 | 133 | @Override |
| ... | ... | @@ -579,12 +583,12 @@ public class SharedSailingDataImpl implements ReplicatingSharedSailingData, Clea |
| 579 | 583 | |
| 580 | 584 | @Override |
| 581 | 585 | public boolean isCurrentlyFillingFromInitialLoad() { |
| 582 | - return currentlyFillingFromInitialLoad.get(); |
|
| 586 | + return currentlyFillingFromInitialLoad; |
|
| 583 | 587 | } |
| 584 | 588 | |
| 585 | 589 | @Override |
| 586 | 590 | public void setCurrentlyFillingFromInitialLoad(boolean currentlyFillingFromInitialLoad) { |
| 587 | - this.currentlyFillingFromInitialLoad.set(currentlyFillingFromInitialLoad); |
|
| 591 | + this.currentlyFillingFromInitialLoad = currentlyFillingFromInitialLoad; |
|
| 588 | 592 | } |
| 589 | 593 | |
| 590 | 594 | @Override |
java/com.sap.sailing.windestimation/src/com/sap/sailing/windestimation/integration/AbstractReplicableWithObjectInputStream.java
| ... | ... | @@ -45,7 +45,7 @@ public abstract class AbstractReplicableWithObjectInputStream<S, O extends Opera |
| 45 | 45 | |
| 46 | 46 | private final Set<OperationWithResult<S, ?>> operationsSentToMasterForReplication; |
| 47 | 47 | |
| 48 | - private ThreadLocal<Boolean> currentlyFillingFromInitialLoad = ThreadLocal.withInitial(() -> false); |
|
| 48 | + private volatile boolean currentlyFillingFromInitialLoad; |
|
| 49 | 49 | |
| 50 | 50 | private ThreadLocal<Boolean> currentlyApplyingOperationReceivedFromMaster = ThreadLocal.withInitial(() -> false); |
| 51 | 51 | |
| ... | ... | @@ -54,6 +54,7 @@ public abstract class AbstractReplicableWithObjectInputStream<S, O extends Opera |
| 54 | 54 | public AbstractReplicableWithObjectInputStream() { |
| 55 | 55 | this.operationsSentToMasterForReplication = new HashSet<>(); |
| 56 | 56 | this.operationExecutionListeners = new ConcurrentHashMap<>(); |
| 57 | + this.currentlyFillingFromInitialLoad = false; |
|
| 57 | 58 | } |
| 58 | 59 | |
| 59 | 60 | @Override |
| ... | ... | @@ -86,12 +87,12 @@ public abstract class AbstractReplicableWithObjectInputStream<S, O extends Opera |
| 86 | 87 | |
| 87 | 88 | @Override |
| 88 | 89 | public boolean isCurrentlyFillingFromInitialLoad() { |
| 89 | - return currentlyFillingFromInitialLoad.get(); |
|
| 90 | + return currentlyFillingFromInitialLoad; |
|
| 90 | 91 | } |
| 91 | 92 | |
| 92 | 93 | @Override |
| 93 | 94 | public void setCurrentlyFillingFromInitialLoad(boolean currentlyFillingFromInitialLoad) { |
| 94 | - this.currentlyFillingFromInitialLoad.set(currentlyFillingFromInitialLoad); |
|
| 95 | + this.currentlyFillingFromInitialLoad = currentlyFillingFromInitialLoad; |
|
| 95 | 96 | } |
| 96 | 97 | |
| 97 | 98 | @Override |
java/com.sap.sailing.www/release_notes_admin.html
| ... | ... | @@ -17,10 +17,10 @@ |
| 17 | 17 | </div> |
| 18 | 18 | </div> |
| 19 | 19 | <div class="yield_content"> |
| 20 | - <div class="contentWrapper"> |
|
| 21 | - <div class="mainContent"> |
|
| 22 | - <h2 class="releaseHeadline">Release Notes - Administration Console</h2> |
|
| 23 | - <div class="innerContent"> |
|
| 20 | + <div class="contentWrapper"> |
|
| 21 | + <div class="mainContent"> |
|
| 22 | + <h2 class="releaseHeadline">Release Notes - Administration Console</h2> |
|
| 23 | + <div class="innerContent"> |
|
| 24 | 24 | <h2 class="articleSubheadline">March 2020</h2> |
| 25 | 25 | <ul class="bulletList"> |
| 26 | 26 | <li>White label: Removal of SAP logos and brand related text from home page and racing panels. Use system property <pre>-Dcom.sap.sailing.debranding=true</pre> |
| ... | ... | @@ -30,6 +30,12 @@ |
| 30 | 30 | time allowances relevant for Performance Curve Scoring (PCS) and offers a link to the |
| 31 | 31 | official PDF version of the respective certificate, based on its reference number. |
| 32 | 32 | </li> |
| 33 | + <li>Improved ESC/Enter key handling in user role/permission editing dialog</li> |
|
| 34 | + <li>The "Replication" panel in the "Advanced" category now has improved information about the |
|
| 35 | + replicables shown. An advanced replica information string may reveal more than just the address, |
|
| 36 | + and the address is now resolved through any <tt>X-Forwarded-For</tt> header fields present |
|
| 37 | + during replica registration, hence also working through load balancers and reverse proxies. |
|
| 38 | + </li> |
|
| 33 | 39 | </ul> |
| 34 | 40 | <h2 class="articleSubheadline">February 2020</h2> |
| 35 | 41 | <ul class="bulletList"> |
java/com.sap.sse.filestorage/src/com/sap/sse/filestorage/impl/FileStorageManagementServiceImpl.java
| ... | ... | @@ -48,7 +48,7 @@ public class FileStorageManagementServiceImpl implements ReplicableFileStorageMa |
| 48 | 48 | private final Map<OperationExecutionListener<ReplicableFileStorageManagementService>, OperationExecutionListener<ReplicableFileStorageManagementService>> operationExecutionListeners = new ConcurrentHashMap<>(); |
| 49 | 49 | private final Set<OperationWithResultWithIdWrapper<?, ?>> operationsSentToMasterForReplication = new HashSet<>(); |
| 50 | 50 | private ReplicationMasterDescriptor replicationMasterDescriptor; |
| 51 | - private ThreadLocal<Boolean> currentlyFillingFromInitialLoad = ThreadLocal.withInitial(() -> false); |
|
| 51 | + private volatile boolean currentlyFillingFromInitialLoad; |
|
| 52 | 52 | |
| 53 | 53 | private ThreadLocal<Boolean> currentlyApplyingOperationReceivedFromMaster = ThreadLocal.withInitial(() -> false); |
| 54 | 54 | |
| ... | ... | @@ -68,6 +68,7 @@ public class FileStorageManagementServiceImpl implements ReplicableFileStorageMa |
| 68 | 68 | |
| 69 | 69 | public FileStorageManagementServiceImpl(TypeBasedServiceFinder<FileStorageService> serviceFinder, |
| 70 | 70 | FileStorageServicePropertyStore propertyStore) { |
| 71 | + this.currentlyFillingFromInitialLoad = false; |
|
| 71 | 72 | this.serviceFinder = serviceFinder; |
| 72 | 73 | this.propertyStore = propertyStore; |
| 73 | 74 | serviceResolver = new FileStorageServiceResolverAgainstOsgiRegistryImpl(serviceFinder); |
| ... | ... | @@ -229,12 +230,12 @@ public class FileStorageManagementServiceImpl implements ReplicableFileStorageMa |
| 229 | 230 | |
| 230 | 231 | @Override |
| 231 | 232 | public boolean isCurrentlyFillingFromInitialLoad() { |
| 232 | - return currentlyFillingFromInitialLoad.get(); |
|
| 233 | + return currentlyFillingFromInitialLoad; |
|
| 233 | 234 | } |
| 234 | 235 | |
| 235 | 236 | @Override |
| 236 | 237 | public void setCurrentlyFillingFromInitialLoad(boolean currentlyFillingFromInitialLoad) { |
| 237 | - this.currentlyFillingFromInitialLoad.set(currentlyFillingFromInitialLoad); |
|
| 238 | + this.currentlyFillingFromInitialLoad = currentlyFillingFromInitialLoad; |
|
| 238 | 239 | } |
| 239 | 240 | |
| 240 | 241 | @Override |
java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/listedit/GenericStringListEditorComposite.java
| ... | ... | @@ -81,10 +81,13 @@ public abstract class GenericStringListEditorComposite<ValueType> extends ListEd |
| 81 | 81 | protected final MultiWordSuggestOracle inputOracle; |
| 82 | 82 | protected final String placeholderTextForAddTextbox; |
| 83 | 83 | protected final Integer inputBoxSize; |
| 84 | + private Button addButton; |
|
| 85 | + private SuggestBox suggestBox; |
|
| 86 | + |
|
| 84 | 87 | public ExpandedUi(StringMessages stringMessages, ImageResource removeImage, Iterable<String> suggestValues) { |
| 85 | 88 | this(stringMessages, removeImage, suggestValues, /* placeholderTextForAddTextbox */ null); |
| 86 | 89 | } |
| 87 | - |
|
| 90 | + |
|
| 88 | 91 | /** |
| 89 | 92 | * @param suggestValues must not be null but may be empty |
| 90 | 93 | * @param placeholderTextForAddTextbox may be null |
| ... | ... | @@ -94,6 +97,15 @@ public abstract class GenericStringListEditorComposite<ValueType> extends ListEd |
| 94 | 97 | this(stringMessages, removeImage, suggestValues, placeholderTextForAddTextbox, /* inputBoxSize */ null); |
| 95 | 98 | |
| 96 | 99 | } |
| 100 | + |
|
| 101 | + public void setEnabled(boolean enabled) { |
|
| 102 | + // #5059 only disabled mode, enabling of button will be handled by internal logic of this component |
|
| 103 | + if (!enabled) { |
|
| 104 | + this.addButton.setEnabled(false); |
|
| 105 | + } |
|
| 106 | + this.suggestBox.setEnabled(enabled); |
|
| 107 | + } |
|
| 108 | + |
|
| 97 | 109 | /** |
| 98 | 110 | * @param inputBoxSize The size of the input box in EM Unit. |
| 99 | 111 | */ |
| ... | ... | @@ -124,11 +136,11 @@ public abstract class GenericStringListEditorComposite<ValueType> extends ListEd |
| 124 | 136 | } |
| 125 | 137 | |
| 126 | 138 | protected SuggestBox createSuggestBox() { |
| 127 | - final SuggestBox result = new SuggestBox(inputOracle); |
|
| 139 | + suggestBox = new SuggestBox(inputOracle); |
|
| 128 | 140 | if (placeholderTextForAddTextbox != null) { |
| 129 | - result.getElement().setAttribute("placeholder", placeholderTextForAddTextbox); |
|
| 141 | + suggestBox.getElement().setAttribute("placeholder", placeholderTextForAddTextbox); |
|
| 130 | 142 | } |
| 131 | - return result; |
|
| 143 | + return suggestBox; |
|
| 132 | 144 | } |
| 133 | 145 | |
| 134 | 146 | @Override |
| ... | ... | @@ -138,7 +150,7 @@ public abstract class GenericStringListEditorComposite<ValueType> extends ListEd |
| 138 | 150 | if (inputBoxSize != null) { |
| 139 | 151 | inputBox.setWidth(Integer.toString(inputBoxSize) + Unit.EM); |
| 140 | 152 | } |
| 141 | - final Button addButton = new Button(getStringMessages().add()); |
|
| 153 | + addButton = new Button(getStringMessages().add()); |
|
| 142 | 154 | addButton.ensureDebugId("AddButton"); |
| 143 | 155 | addButton.setEnabled(false); |
| 144 | 156 | addButton.addClickHandler(new ClickHandler() { |
java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/listedit/StringListEditorComposite.java
| ... | ... | @@ -65,7 +65,9 @@ public class StringListEditorComposite extends GenericStringListEditorComposite< |
| 65 | 65 | } |
| 66 | 66 | |
| 67 | 67 | public void setEnabled(final boolean enabled) { |
| 68 | - // is always of type CollapsedUi (see constructor) |
|
| 69 | - ((CollapsedUi) activeUi).setEnabled(enabled); |
|
| 68 | + if (activeUi instanceof CollapsedUi) |
|
| 69 | + ((CollapsedUi) activeUi).setEnabled(enabled); |
|
| 70 | + if (activeUi instanceof ExpandedUi) |
|
| 71 | + ((ExpandedUi<?>) activeUi).setEnabled(enabled); |
|
| 70 | 72 | } |
| 71 | 73 | } |
java/com.sap.sse.mail/src/com/sap/sse/mail/impl/MailServiceImpl.java
| ... | ... | @@ -48,7 +48,7 @@ public class MailServiceImpl implements ReplicableMailService { |
| 48 | 48 | private ReplicationMasterDescriptor replicatingFromMaster; |
| 49 | 49 | private final ConcurrentMap<OperationExecutionListener<ReplicableMailService>, OperationExecutionListener<ReplicableMailService>> operationExecutionListeners; |
| 50 | 50 | private final Set<OperationWithResultWithIdWrapper<?, ?>> operationsSentToMasterForReplication = new HashSet<>(); |
| 51 | - private ThreadLocal<Boolean> currentlyFillingFromInitialLoad = ThreadLocal.withInitial(() -> false); |
|
| 51 | + private volatile boolean currentlyFillingFromInitialLoad; |
|
| 52 | 52 | |
| 53 | 53 | private ThreadLocal<Boolean> currentlyApplyingOperationReceivedFromMaster = ThreadLocal.withInitial(() -> false); |
| 54 | 54 | |
| ... | ... | @@ -63,6 +63,7 @@ public class MailServiceImpl implements ReplicableMailService { |
| 63 | 63 | private OperationsToMasterSendingQueue unsentOperationForMasterQueue; |
| 64 | 64 | |
| 65 | 65 | public MailServiceImpl(Properties mailProperties, MailServiceResolver mailServiceResolver) { |
| 66 | + this.currentlyFillingFromInitialLoad = false; |
|
| 66 | 67 | this.mailProperties = mailProperties; |
| 67 | 68 | this.operationExecutionListeners = new ConcurrentHashMap<>(); |
| 68 | 69 | this.mailServiceResolver = mailServiceResolver; |
| ... | ... | @@ -238,12 +239,12 @@ public class MailServiceImpl implements ReplicableMailService { |
| 238 | 239 | |
| 239 | 240 | @Override |
| 240 | 241 | public boolean isCurrentlyFillingFromInitialLoad() { |
| 241 | - return currentlyFillingFromInitialLoad.get(); |
|
| 242 | + return currentlyFillingFromInitialLoad; |
|
| 242 | 243 | } |
| 243 | 244 | |
| 244 | 245 | @Override |
| 245 | 246 | public void setCurrentlyFillingFromInitialLoad(boolean currentlyFillingFromInitialLoad) { |
| 246 | - this.currentlyFillingFromInitialLoad.set(currentlyFillingFromInitialLoad); |
|
| 247 | + this.currentlyFillingFromInitialLoad = currentlyFillingFromInitialLoad; |
|
| 247 | 248 | } |
| 248 | 249 | |
| 249 | 250 | @Override |
java/com.sap.sse.replication.interfaces/META-INF/MANIFEST.MF
| ... | ... | @@ -9,7 +9,9 @@ Bundle-RequiredExecutionEnvironment: JavaSE-1.8 |
| 9 | 9 | Export-Package: com.sap.sse.replication, |
| 10 | 10 | com.sap.sse.replication.impl |
| 11 | 11 | Import-Package: com.rabbitmq.client;version="2.8.4", |
| 12 | - org.json.simple |
|
| 12 | + org.json.simple, |
|
| 13 | + org.osgi.framework;version="1.8.0", |
|
| 14 | + org.osgi.util.tracker;version="1.5.1" |
|
| 13 | 15 | Require-Bundle: com.sap.sse.operationaltransformation;bundle-version="1.0.0", |
| 14 | 16 | com.sap.sse.common;bundle-version="1.0.0", |
| 15 | 17 | com.sap.sse;bundle-version="1.0.0" |
java/com.sap.sse.replication.interfaces/src/com/sap/sse/replication/FullyInitializedReplicableTracker.java
| ... | ... | @@ -0,0 +1,134 @@ |
| 1 | +package com.sap.sse.replication; |
|
| 2 | + |
|
| 3 | +import java.util.concurrent.CountDownLatch; |
|
| 4 | +import java.util.concurrent.TimeUnit; |
|
| 5 | + |
|
| 6 | +import org.osgi.framework.BundleContext; |
|
| 7 | +import org.osgi.framework.Filter; |
|
| 8 | +import org.osgi.framework.ServiceReference; |
|
| 9 | +import org.osgi.util.tracker.ServiceTracker; |
|
| 10 | +import org.osgi.util.tracker.ServiceTrackerCustomizer; |
|
| 11 | + |
|
| 12 | +import com.sap.sse.replication.ReplicationService.ReplicationStartingListener; |
|
| 13 | + |
|
| 14 | +/** |
|
| 15 | + * While a regular OSGi {@link ServiceTracker} would {@link ServiceTracker#waitForService(long) wait} for the service's |
|
| 16 | + * appearance in the OSGi registry and then return it, this specialization is aware of the {@link ReplicationService} |
|
| 17 | + * and understands the replication life cycle which can be in {@link ReplicationService#isReplicationStarting() |
|
| 18 | + * starting} mode, and furthermore each replicable can be in the process of handling its initial load. This tracker has |
|
| 19 | + * a specialized {@link #waitForService(long)} implementation that waits for the {@link ReplicationService} to not be in |
|
| 20 | + * state {@link ReplicationService#isReplicationStarting()}, waits for the replicable requested using the regular |
|
| 21 | + * (super-class) {@link ServiceTracker#waitForService(long)} method and asserts the {@link Replicable} to return is |
|
| 22 | + * not in the state {@link Replicable#isCurrentlyFillingFromInitialLoad()}.<p> |
|
| 23 | + * |
|
| 24 | + * By analogy, the {@link #getService()} and related methods return only services for which the above rules apply. |
|
| 25 | + * In particular, {@link #getService()} will return {@code null} if the {@link ReplicationService} is currently in mode |
|
| 26 | + * {@link ReplicationService#isReplicationStarting()} or the replicable found by the regular OSGi tracker is currently |
|
| 27 | + * {@link Replicable#isCurrentlyFillingFromInitialLoad() receiving its initial load}.<p> |
|
| 28 | + * |
|
| 29 | + * Note that {@link #waitForService(long)} must not be used during the activation of a {@link Replicable}'s bundle before |
|
| 30 | + * that {@link Replicable} has been registered with the OSGi registry. Otherwise, a deadlock can result because the waiting |
|
| 31 | + * {@link Replicable} will hold up the completion of the replication start-up and hence wait forever. |
|
| 32 | + * |
|
| 33 | + * @author Axel Uhl (D043530) |
|
| 34 | + * |
|
| 35 | + * @param <R> |
|
| 36 | + * the type of {@link Replicable} to track |
|
| 37 | + */ |
|
| 38 | +public class FullyInitializedReplicableTracker<R extends Replicable<?, ?>> extends ServiceTracker<R, R> { |
|
| 39 | + /** |
|
| 40 | + * Used to find the {@link ReplicationService}. If {@code null}, no attempt will be made to look |
|
| 41 | + * for the {@link ReplicationService}, and only the replicable's {@link Replicable#isCurrentlyFillingFromInitialLoad()} |
|
| 42 | + * result will be considered. |
|
| 43 | + */ |
|
| 44 | + private final ServiceTracker<ReplicationService, ReplicationService> replicationServiceTracker; |
|
| 45 | + |
|
| 46 | + public FullyInitializedReplicableTracker(BundleContext context, Class<R> clazz, |
|
| 47 | + ServiceTrackerCustomizer<R, R> customizer, |
|
| 48 | + ServiceTracker<ReplicationService, ReplicationService> replicationServiceTracker) { |
|
| 49 | + super(context, clazz, customizer); |
|
| 50 | + this.replicationServiceTracker = replicationServiceTracker; |
|
| 51 | + } |
|
| 52 | + |
|
| 53 | + public FullyInitializedReplicableTracker(BundleContext context, Filter filter, |
|
| 54 | + ServiceTrackerCustomizer<R, R> customizer, |
|
| 55 | + ServiceTracker<ReplicationService, ReplicationService> replicationServiceTracker) { |
|
| 56 | + super(context, filter, customizer); |
|
| 57 | + this.replicationServiceTracker = replicationServiceTracker; |
|
| 58 | + } |
|
| 59 | + |
|
| 60 | + public FullyInitializedReplicableTracker(BundleContext context, ServiceReference<R> reference, |
|
| 61 | + ServiceTrackerCustomizer<R, R> customizer, |
|
| 62 | + ServiceTracker<ReplicationService, ReplicationService> replicationServiceTracker) { |
|
| 63 | + super(context, reference, customizer); |
|
| 64 | + this.replicationServiceTracker = replicationServiceTracker; |
|
| 65 | + } |
|
| 66 | + |
|
| 67 | + public FullyInitializedReplicableTracker(BundleContext context, String clazz, |
|
| 68 | + ServiceTrackerCustomizer<R, R> customizer, |
|
| 69 | + ServiceTracker<ReplicationService, ReplicationService> replicationServiceTracker) { |
|
| 70 | + super(context, clazz, customizer); |
|
| 71 | + this.replicationServiceTracker = replicationServiceTracker; |
|
| 72 | + } |
|
| 73 | + |
|
| 74 | + /** |
|
| 75 | + * @param timeoutInMillis |
|
| 76 | + * 0 means indefinite waiting time |
|
| 77 | + * @return {@code true} if the {@link #replicationServiceTracker} is {@code null} or the {@link ReplicationService} |
|
| 78 | + * was obtained successfully and has been in or has reached the state of not |
|
| 79 | + * {@link ReplicationService#isReplicationStarting()} within the timeout provided. {@code false} otherwise. |
|
| 80 | + */ |
|
| 81 | + private boolean waitForReplicationToBeInitialized(long timeoutInMillis) throws InterruptedException { |
|
| 82 | + final boolean result; |
|
| 83 | + if (replicationServiceTracker != null) { |
|
| 84 | + final CountDownLatch latch = new CountDownLatch(1); // counted down either by the direct check or the listener |
|
| 85 | + final ReplicationService replicationService = replicationServiceTracker.waitForService(timeoutInMillis); |
|
| 86 | + final ReplicationStartingListener replicationStartingListener = newIsReplicationStarting->{ |
|
| 87 | + if (!newIsReplicationStarting) { |
|
| 88 | + latch.countDown(); |
|
| 89 | + } |
|
| 90 | + }; |
|
| 91 | + replicationService.addReplicationStartingListener(replicationStartingListener); |
|
| 92 | + if (!replicationService.isReplicationStarting()) { |
|
| 93 | + latch.countDown(); |
|
| 94 | + } |
|
| 95 | + if (timeoutInMillis == 0) { |
|
| 96 | + latch.await(); |
|
| 97 | + result = true; |
|
| 98 | + } else { |
|
| 99 | + result = latch.await(timeoutInMillis, TimeUnit.MILLISECONDS); |
|
| 100 | + } |
|
| 101 | + replicationService.removeReplicationStartingListener(replicationStartingListener); |
|
| 102 | + } else { |
|
| 103 | + result = true; |
|
| 104 | + } |
|
| 105 | + return result; |
|
| 106 | + } |
|
| 107 | + |
|
| 108 | + /** |
|
| 109 | + * Waits for one service object tracked to appear for {@code timeoutInMillis} milliseconds (see |
|
| 110 | + * {@link #waitForService(long)}). If no such service object can be found before timing out, {@code null} |
|
| 111 | + * is returned. Once a service object has been retrieved and a non-{@code null} {@link #replicationServiceTracker} |
|
| 112 | + * has been provided at construction time, the {@link ReplicationService} is obtained from that tracker by |
|
| 113 | + * waiting for it at least {@code timeoutInMillis} milliseconds and then |
|
| 114 | + * |
|
| 115 | + * @param timeoutInMillis |
|
| 116 | + * 0 means indefinite wait time |
|
| 117 | + * @return {@code null} if no service was obtained from the registry in the timeout specified or the replication did |
|
| 118 | + * not reach a fully initialized state in the timeout specified. |
|
| 119 | + */ |
|
| 120 | + public R getInitializedService(long timeoutInMillis) throws InterruptedException { |
|
| 121 | + final R service = waitForService(timeoutInMillis); |
|
| 122 | + final R result; |
|
| 123 | + if (service != null) { |
|
| 124 | + if (waitForReplicationToBeInitialized(timeoutInMillis)) { |
|
| 125 | + result = service; |
|
| 126 | + } else { |
|
| 127 | + result = null; |
|
| 128 | + } |
|
| 129 | + } else { |
|
| 130 | + result = null; |
|
| 131 | + } |
|
| 132 | + return result; |
|
| 133 | + } |
|
| 134 | +} |
java/com.sap.sse.replication.interfaces/src/com/sap/sse/replication/OperationWithResult.java
| ... | ... | @@ -45,6 +45,13 @@ public interface OperationWithResult<S, R> extends Operation<S>, Serializable { |
| 45 | 45 | default boolean isRequiresExplicitTransitiveReplication() { |
| 46 | 46 | return true; |
| 47 | 47 | } |
| 48 | + |
|
| 49 | + /** |
|
| 50 | + * @return the class to use for keeping statistics; this allows wrapper classes to report the actual inner class |
|
| 51 | + */ |
|
| 52 | + default Class<?> getClassForLogging() { |
|
| 53 | + return getClass(); |
|
| 54 | + } |
|
| 48 | 55 | |
| 49 | 56 | /** |
| 50 | 57 | * Ignores the actual result of {@link #internalApplyTo(Object)} and returns <code>toState</code> which |
java/com.sap.sse.replication.interfaces/src/com/sap/sse/replication/OperationWithResultWithIdWrapper.java
| ... | ... | @@ -24,8 +24,7 @@ public class OperationWithResultWithIdWrapper<S, R> implements OperationWithResu |
| 24 | 24 | * Creates a new UUID for this wrapper operation |
| 25 | 25 | */ |
| 26 | 26 | public OperationWithResultWithIdWrapper(OperationWithResult<S, R> delegate) { |
| 27 | - this.delegate = delegate; |
|
| 28 | - this.id = UUID.randomUUID(); |
|
| 27 | + this(delegate, UUID.randomUUID()); |
|
| 29 | 28 | } |
| 30 | 29 | |
| 31 | 30 | /** |
| ... | ... | @@ -37,6 +36,14 @@ public class OperationWithResultWithIdWrapper<S, R> implements OperationWithResu |
| 37 | 36 | this.delegate = delegate; |
| 38 | 37 | } |
| 39 | 38 | |
| 39 | + /** |
|
| 40 | + * @return the {@link #delegate}'s class for keeping statistics |
|
| 41 | + */ |
|
| 42 | + @Override |
|
| 43 | + public Class<?> getClassForLogging() { |
|
| 44 | + return delegate.getClass(); |
|
| 45 | + } |
|
| 46 | + |
|
| 40 | 47 | @Override |
| 41 | 48 | public int hashCode() { |
| 42 | 49 | final int prime = 31; |
java/com.sap.sse.replication.interfaces/src/com/sap/sse/replication/ReplicationService.java
| ... | ... | @@ -113,7 +113,20 @@ public interface ReplicationService { |
| 113 | 113 | */ |
| 114 | 114 | ReplicationReceiver getReplicator(); |
| 115 | 115 | |
| 116 | + /** |
|
| 117 | + * An update to this attribute can be observed by registering a {@link ReplicationStartingListener} using |
|
| 118 | + * the {@link #addReplicationStartingListener} method. |
|
| 119 | + */ |
|
| 116 | 120 | void setReplicationStarting(boolean b); |
| 121 | + |
|
| 122 | + @FunctionalInterface |
|
| 123 | + public static interface ReplicationStartingListener { |
|
| 124 | + void onReplicationStartingChanged(boolean newReplicationStartingValue); |
|
| 125 | + } |
|
| 126 | + |
|
| 127 | + void addReplicationStartingListener(ReplicationStartingListener listener); |
|
| 128 | + |
|
| 129 | + void removeReplicationStartingListener(ReplicationStartingListener listener); |
|
| 117 | 130 | |
| 118 | 131 | /** |
| 119 | 132 | * @return {@code true} if replication is starting; during this phase it is clear that the replicables managed by |
java/com.sap.sse.replication/src/com/sap/sse/replication/impl/Activator.java
| 120 | 133 | index 47e4484..ccdf4e0 |
| 121 | --- a/java/com.sap.sse.replication/src/com/sap/sse/replication/impl/Activator.java |
|
| 134 | +++ b/java/com.sap.sse.replication/src/com/sap/sse/replication/impl/Activator.java |
|
| ... | ... | @@ -175,6 +175,7 @@ public class Activator implements BundleActivator { |
| 175 | 175 | " before firing up replication automatically..."); |
| 176 | 176 | final List<Replicable<?, ?>> replicables = new ArrayList<>(); |
| 177 | 177 | for (String replicableIdAsString : replicableIdsAsStrings) { |
| 178 | + logger.info("Trying to obtain Replicable " + replicableIdAsString+", waiting if necessary"); |
|
| 178 | 179 | Replicable<?, ?> replicable = replicablesProvider.getReplicable(replicableIdAsString, /* wait */true); |
| 179 | 180 | logger.info("Obtained Replicable " + replicableIdAsString); |
| 180 | 181 | replicables.add(replicable); |
java/com.sap.sse.replication/src/com/sap/sse/replication/impl/ReplicationServiceImpl.java
| ... | ... | @@ -19,6 +19,7 @@ import java.util.List; |
| 19 | 19 | import java.util.Map; |
| 20 | 20 | import java.util.Optional; |
| 21 | 21 | import java.util.Random; |
| 22 | +import java.util.Set; |
|
| 22 | 23 | import java.util.Timer; |
| 23 | 24 | import java.util.TimerTask; |
| 24 | 25 | import java.util.UUID; |
| ... | ... | @@ -232,7 +233,7 @@ public class ReplicationServiceImpl implements ReplicationService, OperationsToM |
| 232 | 233 | /** |
| 233 | 234 | * Will be set |
| 234 | 235 | */ |
| 235 | - private boolean replicationStarting; |
|
| 236 | + private volatile boolean replicationStarting; |
|
| 236 | 237 | |
| 237 | 238 | /** |
| 238 | 239 | * An optional link to a persistence layer that allows this service to record replicas |
| ... | ... | @@ -240,6 +241,8 @@ public class ReplicationServiceImpl implements ReplicationService, OperationsToM |
| 240 | 241 | * links can be re-established. |
| 241 | 242 | */ |
| 242 | 243 | private final Optional<MongoObjectFactory> mongoObjectFactory; |
| 244 | + |
|
| 245 | + private final Set<ReplicationStartingListener> replicationStartingListeners; |
|
| 243 | 246 | |
| 244 | 247 | private static class InitialLoadRequest { |
| 245 | 248 | private final Channel channelForInitialLoad; |
| ... | ... | @@ -320,6 +323,7 @@ public class ReplicationServiceImpl implements ReplicationService, OperationsToM |
| 320 | 323 | Optional<MongoObjectFactory> optionalMongoObjectFactory, String exchangeName, String exchangeHost, int exchangePort, |
| 321 | 324 | ReplicationInstancesManager replicationInstancesManager, ReplicablesProvider replicablesProvider) throws IOException { |
| 322 | 325 | this.mongoObjectFactory = optionalMongoObjectFactory; |
| 326 | + this.replicationStartingListeners = new HashSet<>(); |
|
| 323 | 327 | timer = new Timer("ReplicationServiceImpl timer for delayed task sending", /* isDaemon */ true); |
| 324 | 328 | unsentOperationsSenderJob = new UnsentOperationsSenderJob(); |
| 325 | 329 | executionListenersByReplicableIdAsString = new ConcurrentHashMap<>(); |
| ... | ... | @@ -521,7 +525,7 @@ public class ReplicationServiceImpl implements ReplicationService, OperationsToM |
| 521 | 525 | outboundBufferClasses = new ArrayList<>(); |
| 522 | 526 | } |
| 523 | 527 | outboundObjectBuffer.writeObject(serializedOperation); |
| 524 | - outboundBufferClasses.add(operation.getClass()); |
|
| 528 | + outboundBufferClasses.add(operation.getClassForLogging()); |
|
| 525 | 529 | if (outboundBuffer.size() > TRIGGER_MESSAGE_SIZE_IN_BYTES) { |
| 526 | 530 | logger.info("Triggering replication because buffer holds " + outboundBuffer.size() |
| 527 | 531 | + " bytes which exceeds trigger size " + TRIGGER_MESSAGE_SIZE_IN_BYTES + " bytes"); |
| ... | ... | @@ -647,7 +651,7 @@ public class ReplicationServiceImpl implements ReplicationService, OperationsToM |
| 647 | 651 | final Iterable<Replicable<?, ?>> replicables = master.getReplicables(); |
| 648 | 652 | logger.info("Starting to replicate from " + master); |
| 649 | 653 | try { |
| 650 | - registerReplicaWithMaster(master, replicables); |
|
| 654 | + registerReplicaWithMaster(master); |
|
| 651 | 655 | } catch (Exception ex) { |
| 652 | 656 | logger.log(Level.SEVERE, "ERROR", ex); |
| 653 | 657 | throw ex; |
| ... | ... | @@ -680,7 +684,7 @@ public class ReplicationServiceImpl implements ReplicationService, OperationsToM |
| 680 | 684 | final URL initialLoadURL = master.getInitialLoadURL(replicables); |
| 681 | 685 | logger.info("Initial load URL is " + initialLoadURL); |
| 682 | 686 | // start receiving messages already now, but start in suspended mode |
| 683 | - replicator = new ReplicationReceiverImpl(master, replicablesProvider, /* startSuspended */true, consumer); |
|
| 687 | + replicator = new ReplicationReceiverImpl(master, replicablesProvider, /* startSuspended */ true, consumer); |
|
| 684 | 688 | // clear Replicable state here, before starting to receive and de-serialize operations which builds up |
| 685 | 689 | // new state, e.g., in competitor store |
| 686 | 690 | for (Replicable<?, ?> r : replicables) { |
| ... | ... | @@ -733,18 +737,13 @@ public class ReplicationServiceImpl implements ReplicationService, OperationsToM |
| 733 | 737 | } |
| 734 | 738 | |
| 735 | 739 | /** |
| 736 | - * @param replicables |
|
| 737 | - * the replica is registered for these {@link Replicable}s. The master sends operations only for |
|
| 738 | - * replicables that at least one replica has registered for. This may mean that operations are received |
|
| 739 | - * for replicables for which no replicable was requested. Replicas shall drop such operations silently. |
|
| 740 | - * TODO unused... |
|
| 741 | - * |
|
| 742 | 740 | * @return the UUID that the master generated for this client which is also entered into {@link #replicaUUIDs} |
| 743 | 741 | */ |
| 744 | - private String registerReplicaWithMaster(ReplicationMasterDescriptor master, Iterable<Replicable<?, ?>> replicables) throws IOException, |
|
| 742 | + private String registerReplicaWithMaster(ReplicationMasterDescriptor master) throws IOException, |
|
| 745 | 743 | ClassNotFoundException { |
| 746 | 744 | URL replicationRegistrationRequestURL = master.getReplicationRegistrationRequestURL(getServerIdentifier(), |
| 747 | 745 | ServerInfo.getBuildVersion()); |
| 746 | + logger.info("Replication registration request URL: "+replicationRegistrationRequestURL); |
|
| 748 | 747 | final URLConnection registrationRequestConnection = HttpUrlConnectionHelper |
| 749 | 748 | .redirectConnectionWithBearerToken(replicationRegistrationRequestURL, master.getBearerToken()); |
| 750 | 749 | final InputStream content = (InputStream) registrationRequestConnection.getContent(); |
| ... | ... | @@ -761,6 +760,7 @@ public class ReplicationServiceImpl implements ReplicationService, OperationsToM |
| 761 | 760 | } |
| 762 | 761 | } |
| 763 | 762 | final String replicaUUID = uuid.toString(); |
| 763 | + logger.info("Obtained replica UUID "+replicaUUID+" from master"); |
|
| 764 | 764 | registerReplicaUuidForMaster(replicaUUID, master); |
| 765 | 765 | return replicaUUID; |
| 766 | 766 | } |
| ... | ... | @@ -891,9 +891,31 @@ public class ReplicationServiceImpl implements ReplicationService, OperationsToM |
| 891 | 891 | servletPort, bearerToken, replicables); |
| 892 | 892 | } |
| 893 | 893 | |
| 894 | + |
|
| 895 | + @Override |
|
| 896 | + public void addReplicationStartingListener(ReplicationStartingListener listener) { |
|
| 897 | + synchronized (replicationStartingListeners) { |
|
| 898 | + replicationStartingListeners.add(listener); |
|
| 899 | + } |
|
| 900 | + } |
|
| 901 | + |
|
| 894 | 902 | @Override |
| 895 | - public void setReplicationStarting(boolean b) { |
|
| 896 | - this.replicationStarting = b; |
|
| 903 | + public void removeReplicationStartingListener(ReplicationStartingListener listener) { |
|
| 904 | + synchronized (replicationStartingListeners) { |
|
| 905 | + replicationStartingListeners.remove(listener); |
|
| 906 | + } |
|
| 907 | + } |
|
| 908 | + |
|
| 909 | + @Override |
|
| 910 | + public void setReplicationStarting(boolean newReplicationStarting) { |
|
| 911 | + if (this.replicationStarting != newReplicationStarting) { |
|
| 912 | + this.replicationStarting = newReplicationStarting; |
|
| 913 | + synchronized (replicationStartingListeners) { |
|
| 914 | + for (final ReplicationStartingListener listener : replicationStartingListeners) { |
|
| 915 | + listener.onReplicationStartingChanged(newReplicationStarting); |
|
| 916 | + } |
|
| 917 | + } |
|
| 918 | + } |
|
| 897 | 919 | } |
| 898 | 920 | |
| 899 | 921 | @Override |
java/com.sap.sse.security.common/src/com/sap/sse/security/shared/PermissionChecker.java
| ... | ... | @@ -166,8 +166,9 @@ public class PermissionChecker { |
| 166 | 166 | * permission. This is fine when checking for a specific permission like USER:UPDATE:my_user. If one user grants a |
| 167 | 167 | * meta permission like USER:*:* for another user this would mean the current user needs an equivalent meta |
| 168 | 168 | * permission or one that is even more general like *:*:* or USER,USER_GROUP:*:*. In contrast to that, a user who |
| 169 | - * owns the permissions USER:*:* and USER_GROUP:*:* may not grant USER,USER_GROUP:*:* because this meta permission |
|
| 170 | - * is not implied by a single permission held by the current user.<br> |
|
| 169 | + * owns the permissions USER:*:* and USER_GROUP:*:* would---with the <em>regular</em> permission check---not be able |
|
| 170 | + * to grant USER,USER_GROUP:*:* because this meta permission would not be implied by a single permission held by the |
|
| 171 | + * current user.<br> |
|
| 171 | 172 | * To solve this we use the given {@link HasPermissions} instances to resolve wildcards for the type and action |
| 172 | 173 | * parts and construct distinct permissions to check. This means the given {@link HasPermissions} need to be a |
| 173 | 174 | * complete list for the running system. Otherwise we potentially do not check for all required types if the given |
java/com.sap.sse.security.common/src/com/sap/sse/security/shared/impl/Role.java
| ... | ... | @@ -15,10 +15,12 @@ public class Role extends AbstractRole<RoleDefinition, UserGroup, User> { |
| 15 | 15 | } |
| 16 | 16 | |
| 17 | 17 | public Ownership getQualificationAsOwnership() { |
| 18 | + final Ownership result; |
|
| 18 | 19 | if (qualifiedForTenant != null || qualifiedForUser != null) { |
| 19 | - return new Ownership(qualifiedForUser, qualifiedForTenant); |
|
| 20 | + result = new Ownership(qualifiedForUser, qualifiedForTenant); |
|
| 21 | + } else { |
|
| 22 | + result = null; |
|
| 20 | 23 | } |
| 21 | - return null; |
|
| 24 | + return result; |
|
| 22 | 25 | } |
| 23 | - |
|
| 24 | 26 | } |
java/com.sap.sse.security.common/src/com/sap/sse/security/shared/impl/UserGroupImpl.java
| ... | ... | @@ -15,7 +15,7 @@ public class UserGroupImpl extends AbstractUserGroupImpl<User, RoleDefinition> i |
| 15 | 15 | private static final long serialVersionUID = -1290667868080992763L; |
| 16 | 16 | |
| 17 | 17 | public UserGroupImpl(UUID id, String name) { |
| 18 | - super(id, name, new HashSet<User>(), new HashMap<RoleDefinition, Boolean>()); |
|
| 18 | + this(id, name, new HashSet<>(), new HashMap<>()); |
|
| 19 | 19 | } |
| 20 | 20 | |
| 21 | 21 | public UserGroupImpl(UUID id, String name, Set<User> users, Map<RoleDefinition, Boolean> roleDefinitionMap) { |
java/com.sap.sse.security.storemerging/src/com/sap/sse/security/storemerging/SecurityStoreMerger.java
| ... | ... | @@ -137,7 +137,7 @@ public class SecurityStoreMerger { |
| 137 | 137 | replaceSourceOwnershipReferencesToUsersAndGroups(sourceAccessControlStore, userMap, userGroupMap); |
| 138 | 138 | mergePreferences(sourceUserStore, userMap); |
| 139 | 139 | mergeOwnerships(sourceAccessControlStore, ownershipsToTryToImport); |
| 140 | - mergeAccessControlLists(sourceAccessControlStore, userGroupMap); |
|
| 140 | + mergeAccessControlLists(sourceAccessControlStore); |
|
| 141 | 141 | } |
| 142 | 142 | |
| 143 | 143 | private Map<User, User> markUsersForAddMergeOrDrop(UserStore sourceUserStore, AccessControlStore sourceAccessControlStore) { |
| ... | ... | @@ -543,7 +543,11 @@ public class SecurityStoreMerger { |
| 543 | 543 | } |
| 544 | 544 | } |
| 545 | 545 | |
| 546 | - private void mergeAccessControlLists(AccessControlStore sourceAccessControlStore, Map<UserGroup, UserGroup> userGroupMap) { |
|
| 546 | + /** |
|
| 547 | + * The source access control lists are expected to have their groups already updated to point to their corresponding |
|
| 548 | + * groups in the target store. See also {@link #replaceSourceAccessControlListReferencesToGroups(AccessControlStore, Map)}. |
|
| 549 | + */ |
|
| 550 | + private void mergeAccessControlLists(AccessControlStore sourceAccessControlStore) { |
|
| 547 | 551 | logger.info("Applying all source ACLs to target"); |
| 548 | 552 | for (final AccessControlListAnnotation sourceACL : sourceAccessControlStore.getAccessControlLists()) { |
| 549 | 553 | for (final Entry<UserGroup, Set<String>> permissionsPerGroup : sourceACL.getAnnotation().getActionsByUserGroup().entrySet()) { |
java/com.sap.sse.security.test/src/com/sap/sse/security/test/LoginTest.java
| ... | ... | @@ -8,10 +8,17 @@ import static org.junit.Assert.assertSame; |
| 8 | 8 | import static org.junit.Assert.assertTrue; |
| 9 | 9 | |
| 10 | 10 | import java.net.UnknownHostException; |
| 11 | +import java.util.Arrays; |
|
| 11 | 12 | import java.util.Collections; |
| 13 | +import java.util.HashMap; |
|
| 14 | +import java.util.HashSet; |
|
| 15 | +import java.util.Map; |
|
| 16 | +import java.util.Set; |
|
| 12 | 17 | import java.util.UUID; |
| 13 | 18 | |
| 14 | 19 | import org.apache.shiro.SecurityUtils; |
| 20 | +import org.hamcrest.Matchers; |
|
| 21 | +import org.junit.Assert; |
|
| 15 | 22 | import org.junit.Before; |
| 16 | 23 | import org.junit.Test; |
| 17 | 24 | |
| ... | ... | @@ -30,6 +37,8 @@ import com.sap.sse.security.shared.RoleDefinition; |
| 30 | 37 | import com.sap.sse.security.shared.UserGroupManagementException; |
| 31 | 38 | import com.sap.sse.security.shared.UserManagementException; |
| 32 | 39 | import com.sap.sse.security.shared.WildcardPermission; |
| 40 | +import com.sap.sse.security.shared.impl.AccessControlList; |
|
| 41 | +import com.sap.sse.security.shared.impl.QualifiedObjectIdentifierImpl; |
|
| 33 | 42 | import com.sap.sse.security.shared.impl.Role; |
| 34 | 43 | import com.sap.sse.security.shared.impl.User; |
| 35 | 44 | import com.sap.sse.security.shared.impl.UserGroup; |
| ... | ... | @@ -84,6 +93,25 @@ public class LoginTest { |
| 84 | 93 | assertFalse(Util.contains(specialUserGroup2.getUsers(), user)); |
| 85 | 94 | } |
| 86 | 95 | |
| 96 | + |
|
| 97 | + @Test |
|
| 98 | + public void testAclAnonUserGroup() throws UserManagementException, MailException, UserGroupManagementException { |
|
| 99 | + final String username = "TheNewUser"; |
|
| 100 | + securityService.createSimpleUser(username, "u@a.b", "Humba", username, /* company */ null, |
|
| 101 | + /* locale */ null, /* validationBaseURL */ null, /* owning group */ null); |
|
| 102 | + final UserGroup defaultUserGroup = securityService.getUserGroupByName(username + SecurityService.TENANT_SUFFIX); |
|
| 103 | + Map<UserGroup, Set<String>> permissionMap = new HashMap<>(); |
|
| 104 | + permissionMap.put(defaultUserGroup, new HashSet<>(Arrays.asList(new String[] { "!READ", "UPDATE" }))); |
|
| 105 | + permissionMap.put(null, new HashSet<>(Arrays.asList(new String[] { "!READ", "UPDATE" }))); |
|
| 106 | + AccessControlList acl = securityService.updateAccessControlList( |
|
| 107 | + QualifiedObjectIdentifierImpl.fromDBWithoutEscaping("someid/more"), permissionMap); |
|
| 108 | + |
|
| 109 | + Map<UserGroup, Set<String>> result = acl.getActionsByUserGroup(); |
|
| 110 | + |
|
| 111 | + Assert.assertThat(result.get(defaultUserGroup), Matchers.contains("!READ", "UPDATE")); |
|
| 112 | + Assert.assertThat(result.get(null), Matchers.contains("UPDATE")); |
|
| 113 | + } |
|
| 114 | + |
|
| 87 | 115 | @Test |
| 88 | 116 | public void testGetUser() { |
| 89 | 117 | assertNotNull("Subject should not be null: ", SecurityUtils.getSubject()); |
java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/component/editacl/AclEditPanel.java
| ... | ... | @@ -2,6 +2,7 @@ package com.sap.sse.security.ui.client.component.editacl; |
| 2 | 2 | |
| 3 | 3 | import static com.sap.sse.gwt.client.Notification.NotificationType.ERROR; |
| 4 | 4 | import static com.sap.sse.gwt.client.Notification.NotificationType.SUCCESS; |
| 5 | +import static com.sap.sse.gwt.shared.DebugConstants.DEBUG_ID_ATTRIBUTE; |
|
| 5 | 6 | |
| 6 | 7 | import java.util.ArrayList; |
| 7 | 8 | import java.util.Collection; |
| ... | ... | @@ -57,7 +58,7 @@ import com.sap.sse.security.ui.client.i18n.StringMessages; |
| 57 | 58 | public class AclEditPanel extends Composite { |
| 58 | 59 | |
| 59 | 60 | private static AclEditPanelUiBinder uiBinder = GWT.create(AclEditPanelUiBinder.class); |
| 60 | - |
|
| 61 | + |
|
| 61 | 62 | interface AclEditPanelUiBinder extends UiBinder<Widget, AclEditPanel> { |
| 62 | 63 | } |
| 63 | 64 | |
| ... | ... | @@ -104,12 +105,14 @@ public class AclEditPanel extends Composite { |
| 104 | 105 | initWidget(uiBinder.createAndBindUi(this)); |
| 105 | 106 | final CellList<StrippedUserGroupDTO> userGroupList = createUserGroupCellList(); |
| 106 | 107 | userGroupDataProvider.addDataDisplay(userGroupList); |
| 108 | + |
|
| 107 | 109 | userGroupCellListPanelUi.add(wrapIntoCaptionPanel(userGroupList, stringMessages.userGroups(), |
| 108 | 110 | suggestUserGroupUi, addUserGroupButtonUi, removeUserGroupButtonUi)); |
| 109 | 111 | |
| 110 | 112 | // retrieve set of available action names |
| 111 | 113 | final List<String> actionNames = Stream.of(availableActions).map(Action::name).collect(Collectors.toList()); |
| 112 | 114 | |
| 115 | + |
|
| 113 | 116 | // create action editor for allowed actions |
| 114 | 117 | allowedActionsEditor = new StringListEditorComposite(new ArrayList<>(), stringMessages, |
| 115 | 118 | IconResources.INSTANCE.removeIcon(), actionNames, stringMessages.allowedActionName()); |
| ... | ... | @@ -126,8 +129,18 @@ public class AclEditPanel extends Composite { |
| 126 | 129 | permissionsCellListPanelUi.add(deniedActionsContainer = createActionsContainer(stringMessages.deniedActions(), |
| 127 | 130 | deniedActionsEditor, AclDialogResources.INSTANCE.css().deniedActionsTable())); |
| 128 | 131 | |
| 132 | + userGroupSelectionModel.addSelectionChangeHandler(event -> { |
|
| 133 | + StrippedUserGroupDTO userGroup = userGroupSelectionModel.getSelectedObject(); |
|
| 134 | + deniedActionsEditor.setEnabled(userGroup != null && userGroup.getId() != null); |
|
| 135 | + }); |
|
| 136 | + |
|
| 129 | 137 | lblId.setText(id); |
| 130 | 138 | lblType.setText(typeIdentifier); |
| 139 | + |
|
| 140 | + addUserGroupButtonUi.getElement().setAttribute(DEBUG_ID_ATTRIBUTE, "AddUserGroupButton"); |
|
| 141 | + suggestUserGroupUi.getElement().setAttribute(DEBUG_ID_ATTRIBUTE, "SuggestUserGroupInput"); |
|
| 142 | + allowedActionsContainer.getElement().setAttribute(DEBUG_ID_ATTRIBUTE, "allowedActionsContainer"); |
|
| 143 | + deniedActionsContainer.getElement().setAttribute(DEBUG_ID_ATTRIBUTE, "deniedActionsContainer"); |
|
| 131 | 144 | } |
| 132 | 145 | |
| 133 | 146 | private CaptionPanel createActionsContainer(final SafeHtml caption, final Widget content, final String styleName) { |
java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/component/editacl/EditACLDialog.java
| ... | ... | @@ -1,6 +1,7 @@ |
| 1 | 1 | package com.sap.sse.security.ui.client.component.editacl; |
| 2 | 2 | |
| 3 | 3 | import static com.sap.sse.gwt.client.Notification.NotificationType.ERROR; |
| 4 | +import static com.sap.sse.gwt.shared.DebugConstants.DEBUG_ID_ATTRIBUTE; |
|
| 4 | 5 | |
| 5 | 6 | import java.util.function.Consumer; |
| 6 | 7 | import java.util.function.Function; |
| ... | ... | @@ -66,6 +67,8 @@ public class EditACLDialog extends DataEntryDialog<AclDialogResult> { |
| 66 | 67 | aclEditPanel.updateAcl(result); |
| 67 | 68 | } |
| 68 | 69 | }); |
| 70 | + |
|
| 71 | + super.getDialogBox().getElement().setAttribute(DEBUG_ID_ATTRIBUTE, "AclDialog"); |
|
| 69 | 72 | } |
| 70 | 73 | |
| 71 | 74 | @Override |
java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/usermanagement/EditUserRolesAndPermissionsDialog.java
| ... | ... | @@ -35,15 +35,13 @@ public class EditUserRolesAndPermissionsDialog extends DataEntryDialog<Void> { |
| 35 | 35 | super(stringMessages.editRolesAndPermissionsForUser(selectedUsername), null, stringMessages.close(), null, |
| 36 | 36 | /* validator */ null, /* animationEnabled */true, callback); |
| 37 | 37 | final MultiSelectionModel<UserDTO> selectionAdapter = new MultiSelectionModel<>(); |
| 38 | - super.getCancelButton().removeFromParent(); |
|
| 39 | - |
|
| 38 | + super.getCancelButton().setVisible(false); |
|
| 40 | 39 | final Runnable updater = new Runnable() { |
| 41 | 40 | @Override |
| 42 | 41 | public void run() { |
| 43 | 42 | selectionAdapter.clear(); |
| 44 | 43 | userService.getUserManagementService().getRolesAndPermissionsForUser(selectedUsername, |
| 45 | 44 | new AsyncCallback<RolesAndPermissionsForUserDTO>() { |
| 46 | - |
|
| 47 | 45 | @Override |
| 48 | 46 | public void onSuccess(RolesAndPermissionsForUserDTO result) { |
| 49 | 47 | selectionAdapter.clear(); |
| ... | ... | @@ -60,12 +58,10 @@ public class EditUserRolesAndPermissionsDialog extends DataEntryDialog<Void> { |
| 60 | 58 | }); |
| 61 | 59 | } |
| 62 | 60 | }; |
| 63 | - |
|
| 64 | 61 | wildcardPermissionPanel = new WildcardPermissionPanel(userService, stringMessages, errorReporter, |
| 65 | - tableResources, selectionAdapter, updater); |
|
| 62 | + tableResources, selectionAdapter, updater, oracle->createSuggestBox(oracle)); |
|
| 66 | 63 | userRoleDefinitionPanel = new UserRoleDefinitionPanel(userService, stringMessages, errorReporter, |
| 67 | - tableResources, selectionAdapter, updater); |
|
| 68 | - |
|
| 64 | + tableResources, selectionAdapter, updater, oracle->createSuggestBox(oracle), ()->createTextBox("")); |
|
| 69 | 65 | updater.run(); |
| 70 | 66 | } |
| 71 | 67 |
java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/usermanagement/UserManagementPanel.java
| ... | ... | @@ -16,6 +16,7 @@ import com.google.gwt.user.client.ui.Button; |
| 16 | 16 | import com.google.gwt.user.client.ui.DockPanel; |
| 17 | 17 | import com.google.gwt.user.client.ui.HorizontalPanel; |
| 18 | 18 | import com.google.gwt.user.client.ui.ScrollPanel; |
| 19 | +import com.google.gwt.user.client.ui.SuggestBox; |
|
| 19 | 20 | import com.google.gwt.user.client.ui.TextBox; |
| 20 | 21 | import com.google.gwt.user.client.ui.VerticalPanel; |
| 21 | 22 | import com.sap.sse.gwt.client.ErrorReporter; |
| ... | ... | @@ -136,11 +137,11 @@ public class UserManagementPanel<TR extends CellTableWithCheckboxResources> exte |
| 136 | 137 | // add details panel for user roles |
| 137 | 138 | userRoleDefinitionPanel = new UserRoleDefinitionPanel(userService, stringMessages, |
| 138 | 139 | errorReporter, |
| 139 | - tableResources, userList.getSelectionModel(), () -> updateUsers()); |
|
| 140 | + tableResources, userList.getSelectionModel(), () -> updateUsers(), SuggestBox::new, TextBox::new); |
|
| 140 | 141 | detailsPanel.add(userRoleDefinitionPanel); |
| 141 | 142 | // add details panel for user permissions |
| 142 | 143 | final WildcardPermissionPanel userPermissionPanel = new WildcardPermissionPanel(userService, stringMessages, |
| 143 | - errorReporter, tableResources, userList.getSelectionModel(), () -> updateUsers()); |
|
| 144 | + errorReporter, tableResources, userList.getSelectionModel(), () -> updateUsers(), SuggestBox::new); |
|
| 144 | 145 | detailsPanel.add(userPermissionPanel); |
| 145 | 146 | west.add(detailsPanel); |
| 146 | 147 | } |
java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/usermanagement/permissions/WildcardPermissionPanel.java
| ... | ... | @@ -1,5 +1,7 @@ |
| 1 | 1 | package com.sap.sse.security.ui.client.usermanagement.permissions; |
| 2 | 2 | |
| 3 | +import java.util.function.Function; |
|
| 4 | + |
|
| 3 | 5 | import com.google.gwt.core.shared.GWT; |
| 4 | 6 | import com.google.gwt.event.dom.client.ChangeEvent; |
| 5 | 7 | import com.google.gwt.event.dom.client.ChangeHandler; |
| ... | ... | @@ -15,6 +17,7 @@ import com.google.gwt.user.client.ui.HorizontalPanel; |
| 15 | 17 | import com.google.gwt.user.client.ui.MultiWordSuggestOracle; |
| 16 | 18 | import com.google.gwt.user.client.ui.ScrollPanel; |
| 17 | 19 | import com.google.gwt.user.client.ui.SuggestBox; |
| 20 | +import com.google.gwt.user.client.ui.SuggestOracle; |
|
| 18 | 21 | import com.google.gwt.user.client.ui.UIObject; |
| 19 | 22 | import com.google.gwt.user.client.ui.VerticalPanel; |
| 20 | 23 | import com.google.gwt.view.client.MultiSelectionModel; |
| ... | ... | @@ -49,8 +52,8 @@ public class WildcardPermissionPanel extends HorizontalPanel |
| 49 | 52 | |
| 50 | 53 | public WildcardPermissionPanel(final UserService userService, final StringMessages stringMessages, |
| 51 | 54 | final ErrorReporter errorReporter, final CellTableWithCheckboxResources tableResources, |
| 52 | - final MultiSelectionModel<UserDTO> userSelectionModel, final Runnable updateUsers) { |
|
| 53 | - |
|
| 55 | + final MultiSelectionModel<UserDTO> userSelectionModel, final Runnable updateUsers, |
|
| 56 | + Function<SuggestOracle, SuggestBox> suggestBoxConstructor) { |
|
| 54 | 57 | // create multi to single selection adapter |
| 55 | 58 | final SingleSelectionModel<UserDTO> multiToSingleSelectionModelAdapter = new SingleSelectionModel<>(); |
| 56 | 59 | userSelectionModel.addSelectionChangeHandler(event -> { |
| ... | ... | @@ -67,7 +70,6 @@ public class WildcardPermissionPanel extends HorizontalPanel |
| 67 | 70 | } |
| 68 | 71 | }); |
| 69 | 72 | this.userSelectionModel = multiToSingleSelectionModelAdapter; |
| 70 | - |
|
| 71 | 73 | // create suggest for permission |
| 72 | 74 | final MultiWordSuggestOracle oracle = new MultiWordSuggestOracle(); |
| 73 | 75 | for (HasPermissions permission : userService.getAllKnownPermissions()) { |
| ... | ... | @@ -75,17 +77,14 @@ public class WildcardPermissionPanel extends HorizontalPanel |
| 75 | 77 | oracle.add(permission.getStringPermission(action)); |
| 76 | 78 | } |
| 77 | 79 | } |
| 78 | - suggestPermission = new SuggestBox(oracle); |
|
| 80 | + suggestPermission = suggestBoxConstructor.apply(oracle); |
|
| 79 | 81 | roleAndPermissionResources.css().ensureInjected(); |
| 80 | 82 | suggestPermission.addStyleName(roleAndPermissionResources.css().enterPermissionSuggest()); |
| 81 | 83 | suggestPermission.getElement().setPropertyString("placeholder", stringMessages.enterPermissionName()); |
| 82 | 84 | this.initPlaceholder(suggestPermission, stringMessages.enterPermissionName()); |
| 83 | - |
|
| 84 | 85 | // create permission input panel + add controls |
| 85 | 86 | final HorizontalPanel permissionInputPanel = new HorizontalPanel(); |
| 86 | - |
|
| 87 | 87 | permissionInputPanel.add(suggestPermission); |
| 88 | - |
|
| 89 | 88 | final Button addPermissionButton = new Button(stringMessages.add(), (ClickHandler) event -> { |
| 90 | 89 | WildcardPermission selectedPermission = new WildcardPermission(suggestPermission.getText()); |
| 91 | 90 | if (selectedPermission != null) { |
| ... | ... | @@ -93,7 +92,6 @@ public class WildcardPermissionPanel extends HorizontalPanel |
| 93 | 92 | if (selectedUser != null) { |
| 94 | 93 | userService.getUserManagementService().addPermissionForUser(selectedUser.getName(), |
| 95 | 94 | selectedPermission, new AsyncCallback<SuccessInfo>() { |
| 96 | - |
|
| 97 | 95 | @Override |
| 98 | 96 | public void onFailure(Throwable caught) { |
| 99 | 97 | Window.alert(caught.getMessage()); |
| ... | ... | @@ -117,7 +115,6 @@ public class WildcardPermissionPanel extends HorizontalPanel |
| 117 | 115 | suggestPermission.addKeyUpHandler(event -> addPermissionButtonUpdater.execute()); |
| 118 | 116 | suggestPermission.addSelectionHandler(event -> addPermissionButtonUpdater.execute()); |
| 119 | 117 | permissionInputPanel.add(addPermissionButton); |
| 120 | - |
|
| 121 | 118 | // create permission table |
| 122 | 119 | wildcardPermissionWithSecurityDTOTableWrapper = new WildcardPermissionWithSecurityDTOTableWrapper(userService, |
| 123 | 120 | stringMessages, errorReporter, /* enablePager */ true, tableResources, this.userSelectionModel, |
| ... | ... | @@ -126,18 +123,14 @@ public class WildcardPermissionPanel extends HorizontalPanel |
| 126 | 123 | final LabeledAbstractFilterablePanel<WildcardPermissionWithSecurityDTO> userFilterbox = wildcardPermissionWithSecurityDTOTableWrapper |
| 127 | 124 | .getFilterField(); |
| 128 | 125 | userFilterbox.getElement().setPropertyString("placeholder", stringMessages.filterUserGroups()); |
| 129 | - |
|
| 130 | 126 | // add elements to permission panel + add caption |
| 131 | 127 | final VerticalPanel permissionPanel = new VerticalPanel(); |
| 132 | 128 | permissionPanel.add(permissionInputPanel); |
| 133 | 129 | permissionPanel.add(userFilterbox); |
| 134 | 130 | permissionPanel.add(scrollPanel); |
| 135 | - |
|
| 136 | 131 | final CaptionPanel captionPanel = new CaptionPanel(stringMessages.permissions()); |
| 137 | 132 | captionPanel.add(permissionPanel); |
| 138 | - |
|
| 139 | 133 | this.setVisible(false); |
| 140 | - |
|
| 141 | 134 | add(captionPanel); |
| 142 | 135 | } |
| 143 | 136 |
java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/usermanagement/permissions/WildcardPermissionWithSecurityDTOTableWrapper.java
| ... | ... | @@ -68,13 +68,10 @@ public class WildcardPermissionWithSecurityDTOTableWrapper extends |
| 68 | 68 | }, tableResources); |
| 69 | 69 | this.userSelectionModel = userSelectionModel; |
| 70 | 70 | this.userSelectionModel.addSelectionChangeHandler(e -> refreshPermissionList()); |
| 71 | - |
|
| 72 | 71 | final ListHandler<WildcardPermissionWithSecurityDTO> userColumnListHandler = getColumnSortHandler(); |
| 73 | - |
|
| 74 | 72 | // users table |
| 75 | 73 | final TextColumn<WildcardPermissionWithSecurityDTO> userGroupWithSecurityDTONameColumn = new AbstractSortableTextColumn<WildcardPermissionWithSecurityDTO>( |
| 76 | 74 | dto -> dto.toString(), userColumnListHandler); |
| 77 | - |
|
| 78 | 75 | final AccessControlledActionsColumn<WildcardPermissionWithSecurityDTO, PermissionAndRoleImagesBarCell> userActionColumn = create( |
| 79 | 76 | new PermissionAndRoleImagesBarCell(stringMessages), userService); |
| 80 | 77 | userActionColumn.addAction(ACTION_DELETE, DELETE, selectedPermission -> { |
| ... | ... | @@ -82,7 +79,6 @@ public class WildcardPermissionWithSecurityDTOTableWrapper extends |
| 82 | 79 | if (selectedObject != null) { |
| 83 | 80 | userService.getUserManagementService().removePermissionFromUser(selectedObject.getName(), |
| 84 | 81 | selectedPermission, new AsyncCallback<SuccessInfo>() { |
| 85 | - |
|
| 86 | 82 | @Override |
| 87 | 83 | public void onFailure(Throwable caught) { |
| 88 | 84 | Window.alert(stringMessages.couldNotRemovePermissionFromUser(selectedObject.getName(), |
| ... | ... | @@ -103,19 +99,15 @@ public class WildcardPermissionWithSecurityDTOTableWrapper extends |
| 103 | 99 | Window.alert(stringMessages.pleaseSelect()); |
| 104 | 100 | } |
| 105 | 101 | }); |
| 106 | - |
|
| 107 | 102 | final HasPermissions type = SecuredSecurityTypes.PERMISSION_ASSOCIATION; |
| 108 | - |
|
| 109 | 103 | final EditOwnershipDialog.DialogConfig<WildcardPermissionWithSecurityDTO> configOwnership = EditOwnershipDialog |
| 110 | 104 | .create(userService.getUserManagementService(), type, permission -> refreshPermissionList(), |
| 111 | 105 | stringMessages); |
| 112 | - |
|
| 113 | 106 | final EditACLDialog.DialogConfig<WildcardPermissionWithSecurityDTO> configACL = EditACLDialog.create( |
| 114 | 107 | userService.getUserManagementService(), type, user -> user.getAccessControlList(), stringMessages); |
| 115 | 108 | userActionColumn.addAction(ACTION_CHANGE_OWNERSHIP, CHANGE_OWNERSHIP, configOwnership::openDialog); |
| 116 | 109 | userActionColumn.addAction(DefaultActionsImagesBarCell.ACTION_CHANGE_ACL, DefaultActions.CHANGE_ACL, |
| 117 | 110 | permission -> configACL.openDialog(permission)); |
| 118 | - |
|
| 119 | 111 | // filter field configuration |
| 120 | 112 | filterField = new LabeledAbstractFilterablePanel<WildcardPermissionWithSecurityDTO>( |
| 121 | 113 | new Label(stringMessages.filterPermission()), new ArrayList<WildcardPermissionWithSecurityDTO>(), |
| ... | ... | @@ -123,7 +115,7 @@ public class WildcardPermissionWithSecurityDTOTableWrapper extends |
| 123 | 115 | @Override |
| 124 | 116 | public Iterable<String> getSearchableStrings(WildcardPermissionWithSecurityDTO t) { |
| 125 | 117 | List<String> string = new ArrayList<String>(); |
| 126 | - string.add(t.getName()); |
|
| 118 | + string.add(t.toString()); |
|
| 127 | 119 | return string; |
| 128 | 120 | } |
| 129 | 121 | |
| ... | ... | @@ -135,9 +127,7 @@ public class WildcardPermissionWithSecurityDTOTableWrapper extends |
| 135 | 127 | registerSelectionModelOnNewDataProvider(filterField.getAllListDataProvider()); |
| 136 | 128 | filterField |
| 137 | 129 | .setUpdatePermissionFilterForCheckbox(permission -> userService.hasPermission(permission, DefaultActions.UPDATE)); |
| 138 | - |
|
| 139 | 130 | mainPanel.insert(filterField, 0); |
| 140 | - |
|
| 141 | 131 | // setup table |
| 142 | 132 | table.addColumnSortHandler(userColumnListHandler); |
| 143 | 133 | table.addColumn(userGroupWithSecurityDTONameColumn, stringMessages.permission()); |
java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/usermanagement/roles/RoleWithSecurityDTOTableWrapper.java
| ... | ... | @@ -65,13 +65,10 @@ public class RoleWithSecurityDTOTableWrapper extends |
| 65 | 65 | }, tableResources); |
| 66 | 66 | this.userSelectionModel = userSelectionModel; |
| 67 | 67 | this.userSelectionModel.addSelectionChangeHandler(e -> refreshRoleList()); |
| 68 | - |
|
| 69 | 68 | final ListHandler<RoleWithSecurityDTO> userColumnListHandler = getColumnSortHandler(); |
| 70 | - |
|
| 71 | 69 | // users table |
| 72 | 70 | final TextColumn<RoleWithSecurityDTO> userGroupWithSecurityDTONameColumn = new AbstractSortableTextColumn<RoleWithSecurityDTO>( |
| 73 | 71 | dto -> dto.toString(), userColumnListHandler); |
| 74 | - |
|
| 75 | 72 | // add action column |
| 76 | 73 | final AccessControlledActionsColumn<RoleWithSecurityDTO, PermissionAndRoleImagesBarCell> userActionColumn = create( |
| 77 | 74 | new PermissionAndRoleImagesBarCell(stringMessages), userService); |
| ... | ... | @@ -85,7 +82,6 @@ public class RoleWithSecurityDTOTableWrapper extends |
| 85 | 82 | selectedRole.getRoleDefinition().getId(), |
| 86 | 83 | qualifiedForTenant == null ? null : qualifiedForTenant.getName(), |
| 87 | 84 | new AsyncCallback<SuccessInfo>() { |
| 88 | - |
|
| 89 | 85 | @Override |
| 90 | 86 | public void onFailure(Throwable caught) { |
| 91 | 87 | Window.alert(stringMessages.couldNotRemoveRoleFromUser(selectedObject.getName(), |
| ... | ... | @@ -106,25 +102,21 @@ public class RoleWithSecurityDTOTableWrapper extends |
| 106 | 102 | Window.alert(stringMessages.pleaseSelect()); |
| 107 | 103 | } |
| 108 | 104 | }); |
| 109 | - |
|
| 110 | 105 | final HasPermissions type = SecuredSecurityTypes.PERMISSION_ASSOCIATION; |
| 111 | - |
|
| 112 | 106 | final EditOwnershipDialog.DialogConfig<RoleWithSecurityDTO> configOwnership = EditOwnershipDialog |
| 113 | 107 | .create(userService.getUserManagementService(), type, permission -> refreshRoleList(), stringMessages); |
| 114 | - |
|
| 115 | 108 | final EditACLDialog.DialogConfig<RoleWithSecurityDTO> configACL = EditACLDialog.create( |
| 116 | 109 | userService.getUserManagementService(), type, user -> user.getAccessControlList(), stringMessages); |
| 117 | 110 | userActionColumn.addAction(ACTION_CHANGE_OWNERSHIP, CHANGE_OWNERSHIP, configOwnership::openDialog); |
| 118 | 111 | userActionColumn.addAction(DefaultActionsImagesBarCell.ACTION_CHANGE_ACL, DefaultActions.CHANGE_ACL, |
| 119 | 112 | permission -> configACL.openDialog(permission)); |
| 120 | - |
|
| 121 | 113 | // filter field configuration |
| 122 | 114 | filterField = new LabeledAbstractFilterablePanel<RoleWithSecurityDTO>(new Label(stringMessages.filterRoles()), |
| 123 | 115 | new ArrayList<RoleWithSecurityDTO>(), dataProvider, stringMessages) { |
| 124 | 116 | @Override |
| 125 | 117 | public Iterable<String> getSearchableStrings(RoleWithSecurityDTO t) { |
| 126 | 118 | List<String> string = new ArrayList<String>(); |
| 127 | - string.add(t.getName()); |
|
| 119 | + string.add(t.toString()); |
|
| 128 | 120 | return string; |
| 129 | 121 | } |
| 130 | 122 | |
| ... | ... | @@ -135,9 +127,7 @@ public class RoleWithSecurityDTOTableWrapper extends |
| 135 | 127 | }; |
| 136 | 128 | filterField.setUpdatePermissionFilterForCheckbox(role -> userService.hasPermission(role, DefaultActions.UPDATE)); |
| 137 | 129 | registerSelectionModelOnNewDataProvider(filterField.getAllListDataProvider()); |
| 138 | - |
|
| 139 | 130 | mainPanel.insert(filterField, 0); |
| 140 | - |
|
| 141 | 131 | // setup table |
| 142 | 132 | table.addColumnSortHandler(userColumnListHandler); |
| 143 | 133 | table.addColumn(userGroupWithSecurityDTONameColumn, stringMessages.roleName()); |
java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/client/usermanagement/roles/UserRoleDefinitionPanel.java
| ... | ... | @@ -1,5 +1,7 @@ |
| 1 | 1 | package com.sap.sse.security.ui.client.usermanagement.roles; |
| 2 | 2 | |
| 3 | +import java.util.function.Function; |
|
| 4 | +import java.util.function.Supplier; |
|
| 3 | 5 | import java.util.stream.Collectors; |
| 4 | 6 | import java.util.stream.StreamSupport; |
| 5 | 7 | |
| ... | ... | @@ -17,6 +19,7 @@ import com.google.gwt.user.client.ui.CaptionPanel; |
| 17 | 19 | import com.google.gwt.user.client.ui.HorizontalPanel; |
| 18 | 20 | import com.google.gwt.user.client.ui.ScrollPanel; |
| 19 | 21 | import com.google.gwt.user.client.ui.SuggestBox; |
| 22 | +import com.google.gwt.user.client.ui.SuggestOracle; |
|
| 20 | 23 | import com.google.gwt.user.client.ui.TextBox; |
| 21 | 24 | import com.google.gwt.user.client.ui.UIObject; |
| 22 | 25 | import com.google.gwt.user.client.ui.VerticalPanel; |
| ... | ... | @@ -53,8 +56,8 @@ public class UserRoleDefinitionPanel extends HorizontalPanel |
| 53 | 56 | |
| 54 | 57 | public UserRoleDefinitionPanel(final UserService userService, final StringMessages stringMessages, |
| 55 | 58 | final ErrorReporter errorReporter, final CellTableWithCheckboxResources tableResources, |
| 56 | - final MultiSelectionModel<UserDTO> userSelectionModel, final Runnable updateUsers) { |
|
| 57 | - |
|
| 59 | + final MultiSelectionModel<UserDTO> userSelectionModel, final Runnable updateUsers, |
|
| 60 | + Function<SuggestOracle, SuggestBox> suggestBoxConstructor, Supplier<TextBox> textBoxConstructor) { |
|
| 58 | 61 | // create multi to single selection adapter |
| 59 | 62 | final SingleSelectionModel<UserDTO> multiToSingleSelectionModelAdapter = new SingleSelectionModel<>(); |
| 60 | 63 | userSelectionModel.addSelectionChangeHandler(event -> { |
| ... | ... | @@ -71,29 +74,23 @@ public class UserRoleDefinitionPanel extends HorizontalPanel |
| 71 | 74 | } |
| 72 | 75 | }); |
| 73 | 76 | this.userSelectionModel = multiToSingleSelectionModelAdapter; |
| 74 | - |
|
| 75 | 77 | // create role suggest |
| 76 | 78 | oracle = new RoleDefinitionSuggestOracle( |
| 77 | 79 | userService.getUserManagementService(), stringMessages); |
| 78 | - suggestRole = new SuggestBox(oracle); |
|
| 80 | + suggestRole = suggestBoxConstructor.apply(oracle); |
|
| 79 | 81 | roleAndPermissionDetailsResources.css().ensureInjected(); |
| 80 | 82 | suggestRole.addStyleName(roleAndPermissionDetailsResources.css().enterRoleNameSuggest()); |
| 81 | 83 | suggestRole.getElement().setPropertyString("placeholder", stringMessages.enterRoleName()); |
| 82 | 84 | this.initPlaceholder(suggestRole, stringMessages.enterRoleName()); |
| 83 | - |
|
| 84 | 85 | // create role input panel + add controls |
| 85 | 86 | final HorizontalPanel roleInputPanel = new HorizontalPanel(); |
| 86 | - |
|
| 87 | - final TextBox tenantInput = new TextBox(); |
|
| 87 | + final TextBox tenantInput = textBoxConstructor.get(); |
|
| 88 | 88 | this.initPlaceholder(tenantInput, stringMessages.groupName()); |
| 89 | - |
|
| 90 | - final TextBox userInput = new TextBox(); |
|
| 89 | + final TextBox userInput = textBoxConstructor.get(); |
|
| 91 | 90 | this.initPlaceholder(userInput, stringMessages.username()); |
| 92 | - |
|
| 93 | 91 | roleInputPanel.add(suggestRole); |
| 94 | 92 | roleInputPanel.add(tenantInput); |
| 95 | 93 | roleInputPanel.add(userInput); |
| 96 | - |
|
| 97 | 94 | final Button addRoleButton = new Button(stringMessages.add(), (ClickHandler) event -> { |
| 98 | 95 | StrippedRoleDefinitionDTO role = oracle.fromString(suggestRole.getText()); |
| 99 | 96 | if (role != null) { |
| ... | ... | @@ -101,7 +98,6 @@ public class UserRoleDefinitionPanel extends HorizontalPanel |
| 101 | 98 | if (selectedUser != null) { |
| 102 | 99 | userService.getUserManagementService().addRoleToUser(selectedUser.getName(), userInput.getText(), |
| 103 | 100 | role.getId(), tenantInput.getText(), new AsyncCallback<SuccessInfo>() { |
| 104 | - |
|
| 105 | 101 | @Override |
| 106 | 102 | public void onFailure(Throwable caught) { |
| 107 | 103 | Window.alert(caught.getMessage()); |
| ... | ... | @@ -126,7 +122,6 @@ public class UserRoleDefinitionPanel extends HorizontalPanel |
| 126 | 122 | suggestRole.addKeyUpHandler(event -> addRoleButtonUpdater.execute()); |
| 127 | 123 | suggestRole.addSelectionHandler(event -> addRoleButtonUpdater.execute()); |
| 128 | 124 | roleInputPanel.add(addRoleButton); |
| 129 | - |
|
| 130 | 125 | // create role table |
| 131 | 126 | roleWithSecurityDTOTableWrapper = new RoleWithSecurityDTOTableWrapper(userService, stringMessages, |
| 132 | 127 | errorReporter, /* enablePager */ true, tableResources, this.userSelectionModel, updateUsers); |
| ... | ... | @@ -134,18 +129,14 @@ public class UserRoleDefinitionPanel extends HorizontalPanel |
| 134 | 129 | final LabeledAbstractFilterablePanel<RoleWithSecurityDTO> userFilterbox = roleWithSecurityDTOTableWrapper |
| 135 | 130 | .getFilterField(); |
| 136 | 131 | userFilterbox.getElement().setPropertyString("placeholder", stringMessages.filterUserGroups()); |
| 137 | - |
|
| 138 | 132 | // add elements to role panel + add caption |
| 139 | 133 | final VerticalPanel rolePanel = new VerticalPanel(); |
| 140 | 134 | rolePanel.add(roleInputPanel); |
| 141 | 135 | rolePanel.add(userFilterbox); |
| 142 | 136 | rolePanel.add(scrollPanel); |
| 143 | - |
|
| 144 | 137 | final CaptionPanel captionPanel = new CaptionPanel(stringMessages.roles()); |
| 145 | 138 | captionPanel.add(rolePanel); |
| 146 | - |
|
| 147 | 139 | this.setVisible(false); |
| 148 | - |
|
| 149 | 140 | add(captionPanel); |
| 150 | 141 | } |
| 151 | 142 |
java/com.sap.sse.security.ui/src/main/java/com/sap/sse/security/ui/server/UserManagementServiceImpl.java
| ... | ... | @@ -504,7 +504,7 @@ public class UserManagementServiceImpl extends RemoteServiceServlet implements U |
| 504 | 504 | public User call() throws Exception { |
| 505 | 505 | if (userGroupExists(username + SecurityService.TENANT_SUFFIX)) { |
| 506 | 506 | throw new UserManagementException( |
| 507 | - "User tenant already exists, please chose a different username!"); |
|
| 507 | + "User "+username+" already exists, please chose a different username!"); |
|
| 508 | 508 | } |
| 509 | 509 | try { |
| 510 | 510 | User newUser = getSecurityService().createSimpleUser(username, email, password, fullName, |
| ... | ... | @@ -880,7 +880,6 @@ public class UserManagementServiceImpl extends RemoteServiceServlet implements U |
| 880 | 880 | .getQualifiedObjectIdentifier(associationTypeIdentifier); |
| 881 | 881 | getSecurityService().addToAccessControlList(qualifiedObjectAssociationIdentifier, |
| 882 | 882 | null, DefaultActions.READ.name()); |
| 883 | - |
|
| 884 | 883 | getSecurityService().addRoleForUser(user, role); |
| 885 | 884 | logger.info(message); |
| 886 | 885 | } |
java/com.sap.sse.security.userstore.mongodb/src/com/sap/sse/security/userstore/mongodb/AccessControlStoreImpl.java
| ... | ... | @@ -381,7 +381,6 @@ public class AccessControlStoreImpl implements AccessControlStore { |
| 381 | 381 | public Iterable<OwnershipAnnotation> getOwnerships() { |
| 382 | 382 | return LockUtil.executeWithReadLockAndResult(lockForManagementMappings, |
| 383 | 383 | new RunnableWithResult<Iterable<OwnershipAnnotation>>() { |
| 384 | - |
|
| 385 | 384 | @Override |
| 386 | 385 | public Iterable<OwnershipAnnotation> run() { |
| 387 | 386 | return new ArrayList<>(ownerships.values()); |
| ... | ... | @@ -392,7 +391,6 @@ public class AccessControlStoreImpl implements AccessControlStore { |
| 392 | 391 | @Override |
| 393 | 392 | public void clear() { |
| 394 | 393 | LockUtil.executeWithWriteLock(lockForManagementMappings, new Runnable() { |
| 395 | - |
|
| 396 | 394 | @Override |
| 397 | 395 | public void run() { |
| 398 | 396 | accessControlLists.clear(); |
java/com.sap.sse.security/src/com/sap/sse/security/SecurityService.java
| ... | ... | @@ -146,7 +146,7 @@ public interface SecurityService extends ReplicableWithObjectInputStream<Replica |
| 146 | 146 | Iterable<UserGroup> getUserGroupsOfUser(User user); |
| 147 | 147 | |
| 148 | 148 | UserGroup createUserGroup(UUID id, String name) throws UserGroupManagementException; |
| 149 | - |
|
| 149 | + |
|
| 150 | 150 | void addUserToUserGroup(UserGroup group, User user); |
| 151 | 151 | |
| 152 | 152 | void removeUserFromUserGroup(UserGroup group, User user); |
java/com.sap.sse.security/src/com/sap/sse/security/impl/SecurityServiceImpl.java
| 153 | 153 | index 1ad000e..4f0a66e |
| 154 | --- a/java/com.sap.sse.security/src/com/sap/sse/security/impl/SecurityServiceImpl.java |
|
| 154 | +++ b/java/com.sap.sse.security/src/com/sap/sse/security/impl/SecurityServiceImpl.java |
|
| ... | ... | @@ -29,6 +29,7 @@ import java.util.function.Consumer; |
| 29 | 29 | import java.util.function.Function; |
| 30 | 30 | import java.util.logging.Level; |
| 31 | 31 | import java.util.logging.Logger; |
| 32 | +import java.util.stream.Collectors; |
|
| 32 | 33 | |
| 33 | 34 | import javax.servlet.Filter; |
| 34 | 35 | import javax.servlet.ServletContext; |
| ... | ... | @@ -170,7 +171,7 @@ public class SecurityServiceImpl implements ReplicableSecurityService, ClearStat |
| 170 | 171 | |
| 171 | 172 | private Set<OperationWithResultWithIdWrapper<?, ?>> operationsSentToMasterForReplication; |
| 172 | 173 | |
| 173 | - private ThreadLocal<Boolean> currentlyFillingFromInitialLoad = ThreadLocal.withInitial(() -> false); |
|
| 174 | + private volatile boolean currentlyFillingFromInitialLoad; |
|
| 174 | 175 | |
| 175 | 176 | private ThreadLocal<Boolean> currentlyApplyingOperationReceivedFromMaster = ThreadLocal.withInitial(() -> false); |
| 176 | 177 | |
| ... | ... | @@ -239,6 +240,8 @@ public class SecurityServiceImpl implements ReplicableSecurityService, ClearStat |
| 239 | 240 | AccessControlStore accessControlStore, HasPermissionsProvider hasPermissionsProvider, |
| 240 | 241 | String sharedAcrossSubdomainsOf, String baseUrlForCrossDomainStorage) { |
| 241 | 242 | logger.info("Initializing Security Service with user store " + userStore); |
| 243 | + this.currentlyFillingFromInitialLoad = false; |
|
| 244 | + this.currentlyFillingFromInitialLoad = false; |
|
| 242 | 245 | operationsSentToMasterForReplication = new HashSet<>(); |
| 243 | 246 | this.sharedAcrossSubdomainsOf = sharedAcrossSubdomainsOf; |
| 244 | 247 | this.baseUrlForCrossDomainStorage = baseUrlForCrossDomainStorage; |
| ... | ... | @@ -287,12 +290,12 @@ public class SecurityServiceImpl implements ReplicableSecurityService, ClearStat |
| 287 | 290 | |
| 288 | 291 | @Override |
| 289 | 292 | public boolean isCurrentlyFillingFromInitialLoad() { |
| 290 | - return currentlyFillingFromInitialLoad.get(); |
|
| 293 | + return currentlyFillingFromInitialLoad; |
|
| 291 | 294 | } |
| 292 | 295 | |
| 293 | 296 | @Override |
| 294 | 297 | public void setCurrentlyFillingFromInitialLoad(boolean currentlyFillingFromInitialLoad) { |
| 295 | - this.currentlyFillingFromInitialLoad.set(currentlyFillingFromInitialLoad); |
|
| 298 | + this.currentlyFillingFromInitialLoad = currentlyFillingFromInitialLoad; |
|
| 296 | 299 | } |
| 297 | 300 | |
| 298 | 301 | @Override |
| ... | ... | @@ -540,10 +543,14 @@ public class SecurityServiceImpl implements ReplicableSecurityService, ClearStat |
| 540 | 543 | } |
| 541 | 544 | for (Map.Entry<UserGroup, Set<String>> entry : permissionMap.entrySet()) { |
| 542 | 545 | final UserGroup userGroup = entry.getKey(); |
| 546 | + // filter any denied action for anonymous user |
|
| 547 | + Set<String> filteredActions = entry.getValue().stream() |
|
| 548 | + .filter(action -> !(entry.getKey() == null && action != null && action.startsWith("!"))) |
|
| 549 | + .collect(Collectors.toSet()); |
|
| 550 | + |
|
| 543 | 551 | final UUID userGroupId = userGroup == null ? null : userGroup.getId(); |
| 544 | - final Set<String> actions = entry.getValue(); |
|
| 545 | 552 | // avoid the UserGroup object having to be serialized with the operation by using the ID |
| 546 | - apply(s -> s.internalAclPutPermissions(idOfAccessControlledObject, userGroupId, actions)); |
|
| 553 | + apply(s -> s.internalAclPutPermissions(idOfAccessControlledObject, userGroupId, filteredActions)); |
|
| 547 | 554 | } |
| 548 | 555 | return accessControlStore.getAccessControlList(idOfAccessControlledObject).getAnnotation(); |
| 549 | 556 | } |
java/com.sap.sse.security/src/com/sap/sse/security/jaxrs/api/UserGroupResource.java
| ... | ... | @@ -122,8 +122,8 @@ public class UserGroupResource extends AbstractSecurityResource { |
| 122 | 122 | final Response response; |
| 123 | 123 | final JSONObject json = (JSONObject) JSONValue.parse(jsonBody); |
| 124 | 124 | final String groupName = (String) json.get(KEY_GROUP_NAME); |
| 125 | - final UserGroup usergroup = getService().getUserGroupByName(groupName); |
|
| 126 | - if (usergroup != null) { |
|
| 125 | + final UserGroup existingUserGroup = getService().getUserGroupByName(groupName); |
|
| 126 | + if (existingUserGroup != null) { |
|
| 127 | 127 | response = Response.status(Status.BAD_REQUEST).entity("Usergroup with this name already exists.").build(); |
| 128 | 128 | } else { |
| 129 | 129 | UUID newTenantId = UUID.randomUUID(); |
mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/comparators/CompetitorGoalPassingComparator.java
| ... | ... | @@ -10,13 +10,16 @@ import java.util.concurrent.ConcurrentMap; |
| 10 | 10 | import com.sap.sailing.domain.base.Boat; |
| 11 | 11 | import com.sap.sailing.domain.base.Competitor; |
| 12 | 12 | import com.sap.sse.common.Util; |
| 13 | +import com.sap.sse.common.util.NaturalComparator; |
|
| 13 | 14 | |
| 14 | 15 | public class CompetitorGoalPassingComparator implements Comparator<Map.Entry<Competitor, Boat>> { |
| 15 | 16 | |
| 16 | 17 | private ConcurrentMap<UUID, Integer> ranking; |
| 18 | + private final NaturalComparator naturalComparator; |
|
| 17 | 19 | |
| 18 | 20 | public CompetitorGoalPassingComparator() { |
| 19 | 21 | this.ranking = new ConcurrentHashMap<>(); |
| 22 | + this.naturalComparator = new NaturalComparator(); |
|
| 20 | 23 | } |
| 21 | 24 | |
| 22 | 25 | public ConcurrentMap<UUID, Integer> updateWith(List<Util.Pair<Long, String>> sortedList) { |
| ... | ... | @@ -34,6 +37,26 @@ public class CompetitorGoalPassingComparator implements Comparator<Map.Entry<Com |
| 34 | 37 | public int compare(Map.Entry<Competitor, Boat> leftCompetitor, Map.Entry<Competitor, Boat> rightCompetitor) { |
| 35 | 38 | Integer leftRank = this.ranking.get(leftCompetitor.getKey().getId()); |
| 36 | 39 | Integer rightRank = this.ranking.get(rightCompetitor.getKey().getId()); |
| 40 | + if (leftRank == null && rightRank == null) { |
|
| 41 | + //Compare further properties |
|
| 42 | + //Sail ID |
|
| 43 | + int result = CompetitorSailIdComparator.compare(leftCompetitor.getValue().getSailID(), rightCompetitor.getValue().getSailID(), naturalComparator); |
|
| 44 | + if (result == 0) { |
|
| 45 | + //Short name |
|
| 46 | + result = CompetitorShortNameComparator.compare(leftCompetitor.getKey().getShortName(), rightCompetitor.getKey().getShortName(), naturalComparator); |
|
| 47 | + } |
|
| 48 | + if (result == 0) { |
|
| 49 | + //Competitor name |
|
| 50 | + result = naturalComparator.compare(leftCompetitor.getKey().getName(), rightCompetitor.getKey().getName()); |
|
| 51 | + } |
|
| 52 | + return result; |
|
| 53 | + } |
|
| 54 | + if (leftRank == null) { |
|
| 55 | + return 1; |
|
| 56 | + } |
|
| 57 | + if (rightRank == null) { |
|
| 58 | + return -1; |
|
| 59 | + } |
|
| 37 | 60 | return leftRank - rightRank; |
| 38 | 61 | } |
| 39 | 62 | } |
wiki/info/landscape/amazon-ec2.md
| ... | ... | @@ -12,82 +12,22 @@ |
| 12 | 12 | |
| 13 | 13 | #### Starting an instance |
| 14 | 14 | |
| 15 | +To start with, your user account needs to have sufficient permissions to create a new server group ``{NEWSERVERNAME}-server`` up-front so that you have at least the permissions granted by the ``user`` role for all objects owned by that group. Change the group's group ownership so that the new group is its own group owner. Additionally, in order to have the new server participate in the shared security service and shared sailing data service on ``security-service.sapsailing.com`` your user needs ``SERVER:REPLICATE:security-service``. Your user should also have the ``SERVER:*:{NEWSERVERNAME}`` permission (e.g., implied by the more general ``SERVER:*`` permission), e.g., granted by the ``server_admin`` role. The latter permission is helpful in order to be able to configure the resulting server and to set up replication for it. If your user account currently does not have those permissions, find an administrator who has at least ``SERVER:*`` which is implied in particular by having role ``server_admin:*``. Such an administrator will be able to grant you the ``SERVER``-related permissions described here. |
|
| 15 | 16 | |
| 16 | -- Which instance type to choose: |
|
| 17 | - - Archive: m2.2xlarge |
|
| 18 | - - Live: c1.xlarge |
|
| 17 | +Now start by creating the new server group, named ``{NEWSERVERNAME}-server``. So for example, if your server will use ``SERVER_NAME=abc`` then create a user group called ``abc-server``. You will yourself be a member of that new group automatically. Add role ``user`` to the group, enabling it only for the members of the group ("Enabled for all users" set to "No"). This way, all members of the group will gain permissions for objects owned by that server as if they owned them themselves. This also goes for the new ``SERVER`` object, but owners only obtain permissions for default actions, not the dedicated ``SERVER`` actions. |
|
| 19 | 18 | |
| 20 | -You may need to select "All generations" instead of "Current generation" to see these instance configurations. Of course, you may choose variations of those as you feel is appropriate for your use case. |
|
| 21 | - |
|
| 22 | -- Using a release, set the following in the instance's user data, replacing `myspecificevent` by a unique name of the event or series you'll be running on that instance, such as `kielerwoche2014` or similar. |
|
| 23 | - |
|
| 24 | - ``` |
|
| 25 | - INSTALL_FROM_RELEASE=`name-of-release` |
|
| 26 | - USE_ENVIRONMENT=live-server |
|
| 27 | - MONGODB_URI="mongodb://mongo0.internal.sapsailing.com,mongo1.internal.sapsailing.com/myspecificevent?replicaSet=live&retryWrites=true" |
|
| 28 | - REPLICATION_CHANNEL=myspecificevent |
|
| 29 | - SERVER_NAME=MYSPECIFICEVENT |
|
| 30 | - BUILD_COMPLETE_NOTIFY=your@email.here |
|
| 31 | - SERVER_STARTUP_NOTIFY=your@email.here |
|
| 32 | - ADDITIONAL_JAVA_ARGS="$ADDITIONAL_JAVA_ARGS -Dcom.sap.sailing.domain.tracking.MailInvitationType=SailInsight2" |
|
| 33 | - # Provide authentication credentials for a user on security-service.sapsailing.com permitted to replicate, either by username/password... |
|
| 34 | - #REPLICATE_MASTER_USERNAME=(user for replicator login on security-service.sapsailing.com server having SERVER:REPLICATE:<server-name> permission) |
|
| 35 | - #REPLICATE_MASTER_PASSWORD=(password of the user for replication login on security-service.sapsailing.com) |
|
| 36 | - # Or by bearer token, obtained, e.g., through |
|
| 37 | - # curl -d "username=myuser&password=mysecretpassword" "https://security-service.sapsailing.com/security/api/restsecurity/access_token" | jq .access_token |
|
| 38 | - # or by logging in to the security-service.sapsailing.com server using your web browser and then navigating to |
|
| 39 | - # https://security-service.sapsailing.com/security/api/restsecurity/access_token |
|
| 40 | - #REPLICATE_MASTER_BEARER_TOKEN= |
|
| 41 | -``` |
|
| 42 | - |
|
| 43 | -The *MailInvitationType* property controls which version of the SAP Sail Insight app will be targeted by tracking invitations sent out by e-mail. |
|
| 44 | -Two different Branch.io URL schemes exist for the Sail Insight app: sailinsight-app.sapsailing.com and sailinsight20-app.sapsailing.com. |
|
| 45 | -They can be selected by providing *SailInsight1* or *SailInsight2*, respectively, as the values for the property. If the property is |
|
| 46 | -set to *LEGACY*, no Branch.io link is used in the invitation at all. This mode should no longer be used because the Branch.io-enabled |
|
| 47 | -iOS app has hit the store. If not provided, it will default to *SailInsight2*. |
|
| 48 | - |
|
| 49 | -Note that when you select to install an environment using the `USE_ENVIRONMENT` variable, any other variable that you specify in the user data, such as the `MONGODB_URI` or `REPLICATION_CHANNEL` properties in the example above, these additional user data properties will override whatever comes from the environment specified by the `USE_ENVIRONMENT` parameter. |
|
| 50 | - |
|
| 51 | -- To build from git, install and start, set the following in the instance's user data, adjusting the branch name (`BUILD_FROM`), the `myspecificevent` naming and memory settings according to your needs: |
|
| 52 | - |
|
| 53 | -``` |
|
| 54 | - BUILD_BEFORE_START=True |
|
| 55 | - BUILD_FROM=master |
|
| 56 | - RUN_TESTS=False |
|
| 57 | - COMPILE_GWT=True |
|
| 58 | - BUILD_COMPLETE_NOTIFY=you@email.com |
|
| 59 | - SERVER_STARTUP_NOTIFY= |
|
| 60 | - SERVER_NAME=MYSPECIFICEVENT |
|
| 61 | - MEMORY=2048m |
|
| 62 | - REPLICATION_HOST=rabbit.internal.sapsailing.com |
|
| 63 | - REPLICATION_CHANNEL=myspecificevent |
|
| 64 | - MONGODB_URI="mongodb://mongo0.internal.sapsailing.com,mongo1.internal.sapsailing.com/myspecificevent?replicaSet=live&retryWrites=true" |
|
| 65 | - # Provide authentication credentials for a user on security-service.sapsailing.com permitted to replicate, either by username/password... |
|
| 66 | - #REPLICATE_MASTER_USERNAME=(user for replicator login on security-service.sapsailing.com server having SERVER:REPLICATE:<server-name> permission) |
|
| 67 | - #REPLICATE_MASTER_PASSWORD=(password of the user for replication login on security-service.sapsailing.com) |
|
| 68 | - # Or by bearer token, obtained, e.g., through |
|
| 69 | - # curl -d "username=myuser&password=mysecretpassword" "https://security-service.sapsailing.com/security/api/restsecurity/access_token" | jq .access_token |
|
| 70 | - # or by logging in to the security-service.sapsailing.com server using your web browser and then navigating to |
|
| 71 | - # https://security-service.sapsailing.com/security/api/restsecurity/access_token |
|
| 72 | - #REPLICATE_MASTER_BEARER_TOKEN= |
|
| 73 | -``` |
|
| 74 | - |
|
| 75 | -#### Setting up a new image (AMI) from scratch (more or less) |
|
| 76 | - |
|
| 77 | -See [here](/wiki/creating-ec2-image-from-scratch) |
|
| 78 | - |
|
| 79 | -#### Receiving wind from Expedition |
|
| 19 | +Now choose the instance type to start. For example: |
|
| 20 | + - Archive server: i3.2xlarge |
|
| 21 | + - Live event: c4.2xlarge |
|
| 80 | 22 | |
| 81 | -- To receive and forward wind with an Expedition connector, log into webserver as user trac and switch to $HOME/servers/udpmirror. Start the mirror and forward it to the instance you want. In order to receive wind through the Igtimi connector, this step is not required as the wind data is received directly from the Igtimi server. |
|
| 82 | - |
|
| 83 | -#### Setting up Master and Replica |
|
| 23 | +You may need to select "All generations" instead of "Current generation" to see these instance configurations. Of course, you may choose variations of those as you feel is appropriate for your use case. |
|
| 84 | 24 | |
| 85 | -- Fire up a master with the following configuration. There is a preconfigured master environment at http://releases.sapsailing.com/environments/live-master-server that you should use. |
|
| 25 | +Using a release, set the following in the instance's user data, replacing `myspecificevent` by a unique name of the event or series you'll be running on that instance, such as `kielerwoche2014` or similar. Note that when you select to install an environment using the `USE_ENVIRONMENT` variable, any other variable that you specify in the user data, such as the `MONGODB_URI` or `REPLICATION_CHANNEL` properties in the example above, these additional user data properties will override whatever comes from the environment specified by the `USE_ENVIRONMENT` parameter. |
|
| 86 | 26 | |
| 87 | 27 | ``` |
| 88 | 28 | INSTALL_FROM_RELEASE=(name-of-release) |
| 89 | 29 | USE_ENVIRONMENT=live-master-server |
| 90 | -SERVER_NAME=MYSPECIFICEVENT |
|
| 30 | +SERVER_NAME=myspecificevent |
|
| 91 | 31 | REPLICATION_CHANNEL=myspecificevent |
| 92 | 32 | MONGODB_URI="mongodb://mongo0.internal.sapsailing.com,mongo1.internal.sapsailing.com/myspecificevent?replicaSet=live&retryWrites=true" |
| 93 | 33 | SERVER_STARTUP_NOTIFY=you@email.com |
| ... | ... | @@ -98,17 +38,22 @@ SERVER_STARTUP_NOTIFY=you@email.com |
| 98 | 38 | # curl -d "username=myuser&password=mysecretpassword" "https://security-service.sapsailing.com/security/api/restsecurity/access_token" | jq .access_token |
| 99 | 39 | # or by logging in to the security-service.sapsailing.com server using your web browser and then navigating to |
| 100 | 40 | # https://security-service.sapsailing.com/security/api/restsecurity/access_token |
| 101 | -#REPLICATE_MASTER_BEARER_TOKEN= |
|
| 41 | +REPLICATE_MASTER_BEARER_TOKEN=(a bearer token allowing this master to replicate from security-service.sapsailing.com) |
|
| 102 | 42 | ``` |
| 103 | 43 | |
| 104 | -- After your master server is ready, note the internal IP and configure your replica instances. Set up a user account there that has the following permission: ``SERVER:REPLICATE:{SERVERNAME}``. You will need this user's credentials to authenticate your replicas for replication. |
|
| 44 | +Have at least a public-facing target group ready. If you want to expose the master to the public (single-instance scenario or master-replica scenario where the master also handles reading client requests) add the master to the public target group. |
|
| 105 | 45 | |
| 106 | -- Make sure to use the preconfigured environment from http://releases.sapsailing.com/environments/live-replica-server. Then absolutely make sure to add the line "REPLICATE_MASTER_SERVLET_HOST" to the user-data and adjust the `myspecificevent` master exchange name to the `REPLICATION_CHANNEL` setting you used for the master configuration. |
|
| 46 | +If you want to launch one or more replicas, ensure you have a dedicated ``...-master`` target group to which you add your master instance, and a load balancer rule that forwards your replica's requests directed to the master to that ``...-master`` target group, for example, by using a dedicated ``...-master`` hostname rule in your load balancer which then forwards to the ``...-master`` target group. |
|
| 47 | + |
|
| 48 | +After your master server is ready, note the internal IP and configure your replica instances if you'd like to connect using the master's IP address. Alternatively, you may route the replica requests through the load balancer again, using whatever your load balancer requires to route the requests to your master, such as the ``...-master`` hostname with HTTPS as a protocol and 443 for a port. If you don't want to use the credentials of your own user account (which is expected to have permission ``SERVER:REPLICATE:{SERVERNAME}`` already because as described above you need this for configuring the new server), e.g., because you then have to expose an access token in the environment that anyone with SSH access to the instance may be able to see, set up a new user account, such as ``{SERVERNAME}-replicator``, that has the following permission: ``SERVER:REPLICATE:{SERVERNAME}`` where ``{SERVERNAME}`` is what you provided above for the ``SERVER_NAME`` environment variable. You will be able to grant this permission to the new user because your own user account is expected to have this permission. You will need your own or this new user's credentials to authenticate your replicas for replication. |
|
| 49 | + |
|
| 50 | +Make sure to use the preconfigured environment from http://releases.sapsailing.com/environments/live-replica-server. Then absolutely make sure to add the line "REPLICATE_MASTER_SERVLET_HOST" to the user-data and adjust the `myspecificevent` master exchange name in the replica's ``REPLICATE_MASTER_EXCHANGE_NAME`` variable to the value of the ``REPLICATION_CHANNEL`` setting you used for the master configuration. Also ensure that you provide the ``REPLICATE_MASTER_BEARER_TOKEN`` value (or, alternatively ``REPLICATE_MASTER_USERNAME`` and ``REPLICATE_MASTER_PASSWORD``) to grant the replica the permissions it needs to successfully register with the master as a replica. |
|
| 107 | 51 | |
| 108 | 52 | ``` |
| 109 | 53 | INSTALL_FROM_RELEASE=(name-of-release) |
| 110 | 54 | USE_ENVIRONMENT=live-replica-server |
| 111 | -REPLICATE_MASTER_SERVLET_HOST=(IP of your master server) |
|
| 55 | +REPLICATE_MASTER_SERVLET_HOST=(IP of your master server or external -master hostname) |
|
| 56 | +REPLICATE_MASTER_SERVLET_PORT=(port your master is listening on for HTTP/HTTPS requests; defaults to 8888; use 443 for -master hostname) |
|
| 112 | 57 | REPLICATE_MASTER_EXCHANGE_NAME=myspecificevent |
| 113 | 58 | # Provide authentication credentials for a user on the master permitted to replicate, either by username/password... |
| 114 | 59 | #REPLICATE_MASTER_USERNAME=(user for replicator login on master server having SERVER:REPLICATE:<server-name> permission) |
| ... | ... | @@ -117,13 +62,21 @@ REPLICATE_MASTER_EXCHANGE_NAME=myspecificevent |
| 117 | 62 | # curl -d "username=myuser&password=mysecretpassword" "https://master-server.sapsailing.com/security/api/restsecurity/access_token" | jq .access_token |
| 118 | 63 | # or by logging in to the master server using your web browser and then navigating to |
| 119 | 64 | # https://master-server.sapsailing.com/security/api/restsecurity/access_token |
| 120 | -# REPLICATE_MASTER_BEARER_TOKEN= |
|
| 65 | +REPLICATE_MASTER_BEARER_TOKEN=(a bearer token allowing this master to replicate from your master) |
|
| 121 | 66 | SERVER_NAME=MYSPECIFICEVENT |
| 122 | 67 | MONGODB_URI="mongodb://mongo0.internal.sapsailing.com,mongo1.internal.sapsailing.com/myspecificevent-replica?replicaSet=live&retryWrites=true" |
| 123 | 68 | EVENT_ID=<some-uuid-of-an-event-you-want-to-feature> |
| 124 | 69 | SERVER_STARTUP_NOTIFY=you@email.com |
| 125 | 70 | ``` |
| 126 | 71 | |
| 72 | +#### Setting up a new image (AMI) from scratch (more or less) |
|
| 73 | + |
|
| 74 | +See [here](/wiki/creating-ec2-image-from-scratch) |
|
| 75 | + |
|
| 76 | +#### Receiving wind from Expedition |
|
| 77 | + |
|
| 78 | +- To receive and forward wind with an Expedition connector, log into webserver as user trac and switch to $HOME/servers/udpmirror. Start the mirror and forward it to the instance you want. In order to receive wind through the Igtimi connector, this step is not required as the wind data is received directly from the Igtimi server. |
|
| 79 | + |
|
| 127 | 80 | #### Setting up a Multi Instance |
| 128 | 81 | To set up a multi instance for a server with name "SSV", subdomain "ssv.sapsailing.com" and description "Schwartauer Segler-Verein, [www.ssv-net.de](http://www.ssv-net.de), Alexander Probst, [webmaster@alexprobst.de](mailto:webmaster@alexprobst.de)" perform the following steps: |
| 129 | 82 |
wiki/info/security/permission-concept.md
| ... | ... | @@ -100,14 +100,16 @@ Additionally, on fresh instances not sharing a UserStore, a set of default roles |
| 100 | 100 | |
| 101 | 101 | ### Role "admin" |
| 102 | 102 | |
| 103 | -This role implies the "*" permission. It should ideally be used with a user group ownership qualification, such as the server-specific group, making a user with a role qualified in this way an administrator for the server object as well as for all other objects owned by that server group. |
|
| 103 | +This role implies the "*" permission. It should ideally be used with a user group ownership qualification, such as the server-specific group, making a user with a role qualified in this way an administrator for the server object as well as for all other objects owned by that server group. The ``admin`` role could also be used together with a user qualification, as in ``admin::{username}`` to grant a user full permissions to all objects owned by that user. Note that this in the future, as we introduce payed premium content, would include such premium permissions. Likewise, the ``admin`` role qualified to the user's default group ``{username}-tenant`` could be granted to users so they obtain full rights for all objects owned by their default ``{username}-tenant`` group. |
|
| 104 | 104 | |
| 105 | 105 | ### Role "user" |
| 106 | 106 | |
| 107 | 107 | This role is intended to be used to describe the permissions that object owners shall be granted. In particular, when qualified |
| 108 | 108 | for objects owned by the user being assigned the role, that user obtains the permission to execute actions |
| 109 | 109 | ``CHANGE_ACL,CHANGE_OWNERSHIP,CREATE,DELETE,READ,READ_PUBLIC,UPDATE`` for objects of any type. By default, users are also assigned |
| 110 | -this role constrained for objects whose group owner is the user's dedicated group (``{username}-tenant``). |
|
| 110 | +this role qualified for objects whose group owner is the user's dedicated group (``{username}-tenant``). |
|
| 111 | + |
|
| 112 | +Note that for good reasons this role does not imply all ("*") permissions to object owners. All permissions would include features that we may want to charge a price for. The ``user`` role shall include only those permissions that all users with no premium permissions have. In a sense, the user role's permission set is the difference of the admin role's permission set ("*") minus all premium permissions. |
|
| 111 | 113 | |
| 112 | 114 | ### Role "sailing_viewer" |
| 113 | 115 |
wiki/info/security/security.md
| ... | ... | @@ -1,6 +1,6 @@ |
| 1 | 1 | # Security |
| 2 | 2 | |
| 3 | -The Sports Sponsorships Engine (SSE) on which the SAP Sailing Analytics and the SAP Tennis Analytics are based, uses Apache Shiro to implement security. This in particular includes authentication and authorization. This document does not aim to replace the Shiro documentation which is available, e.g., at [http://shiro.apache.org/configuration.html](http://shiro.apache.org/configuration.html), [http://shiro.apache.org/permissions.html](http://shiro.apache.org/permissions.html) and [http://shiro.apache.org/web.html](http://shiro.apache.org/web.html). |
|
| 3 | +The Sports Sponsorships Engine (SSE) on which the SAP Sailing Analytics and the SAP Tennis Analytics are based, uses Apache Shiro to implement security. This in particular includes authentication and authorization. This document does not aim to replace the Shiro documentation which is available, e.g., at [http://shiro.apache.org/configuration.html](http://shiro.apache.org/configuration.html), [http://shiro.apache.org/permissions.html](http://shiro.apache.org/permissions.html) and [http://shiro.apache.org/web.html](http://shiro.apache.org/web.html). It relies on a sophisticated and powerful security engine that is part of SSE and whose [permission concept is described here](permission-concept). |
|
| 4 | 4 | |
| 5 | 5 | ## Users, Sessions, Roles, and Permissions |
| 6 | 6 | |
| ... | ... | @@ -58,11 +58,23 @@ There are generally two ways in which some feature can require the user to be eq |
| 58 | 58 | |
| 59 | 59 | Example for a declarative permission check: |
| 60 | 60 | [urls] |
| 61 | - /api/v1/events = bearerToken, perms["event:view"] |
|
| 61 | + /api/v1/events = bearerToken, perms["EVENT:READ"] |
|
| 62 | 62 | This requires users trying to access the URL `/api/v1/events` to be authenticated using a valid `JSESSIONID` cookie or any authentication supported by the `bearerToken` filter such that the authenticated user has permissions that imply the `event:view:*` permission. |
| 63 | 63 | |
| 64 | -Example for a programmatic check: |
|
| 65 | - SecurityUtils.getSubject().checkPermission("event:view"); |
|
| 64 | +Examples for programmatic checks if the domain object, here ``event`` is at hand: |
|
| 65 | + |
|
| 66 | +``` |
|
| 67 | + SecurityUtils.getSubject().isPermitted(SecuredDomainType.EVENT.getStringPermissionForObject(DefaultActions.READ, event)) |
|
| 68 | +``` |
|
| 69 | + |
|
| 70 | +or |
|
| 71 | + |
|
| 72 | +``` |
|
| 73 | + SecurityUtils.getSubject().isPermitted(SecuredDomainType.EVENT.getStringPermissionForTypeRelativeIdentifier( |
|
| 74 | + DefaultActions.UPDATE, EventBaseImpl.getTypeRelativeObjectIdentifier(eventId))) |
|
| 75 | +``` |
|
| 76 | + |
|
| 77 | +if only an ID such as ``eventId`` is available at the point of the permission check. |
|
| 66 | 78 | |
| 67 | 79 | ### Special Case: Permission Checks in the AdminConsole |
| 68 | 80 |