serialize cached image sizes with EventBase so they are available in GWT front-end to decide what goes to the stage and what not

This commit is contained in:
Axel Uhl committed 2014-08-25 22:49:00 +02:00
1 parent da14d835c7
commit 1b46a87d79
9 files changed
+131 -18

No files matched your search

@@ -1,6 +1,7 @@
package com.sap.sailing.domain.base;
import java.net.URL;
import java.util.concurrent.ExecutionException;
import com.sap.sailing.domain.common.Renamable;
import com.sap.sailing.domain.common.TimePoint;
@@ -119,4 +120,15 @@ public interface EventBase extends Named, WithDescription, Renamable, WithID {
void setOfficialWebsiteURL(URL officialWebsiteURL);
Iterable<? extends LeaderboardGroupBase> getLeaderboardGroups();
public static interface ImageSize {
int getWidth();
int getHeight();
}
/**
* For the images references by the image URLs in {@link #getImageURLs()}, {@link #getSponsorImageURLs()} and {@link #getLogoImageURL()}
* determines the image dimensions.
*/
ImageSize getImageSize(URL imageURL) throws InterruptedException, ExecutionException;
}
@@ -220,4 +220,27 @@ public abstract class EventBaseImpl implements EventBase {
public void setOfficialWebsiteURL(URL officialWebsiteURL) {
this.officialWebsiteURL = officialWebsiteURL;
}
public static class ImageSizeImpl implements ImageSize {
private final int width;
private final int height;
public ImageSizeImpl(int width, int height) {
super();
this.width = width;
this.height = height;
}
@Override
public int getWidth() {
return width;
}
@Override
public int getHeight() {
return height;
}
@Override
public String toString() {
return "("+getWidth()+"x"+getHeight()+")";
}
}
}
@@ -1,6 +1,10 @@
package com.sap.sailing.domain.base.impl;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ExecutionException;
import com.sap.sailing.domain.base.EventBase;
import com.sap.sailing.domain.base.LeaderboardGroupBase;
@@ -9,7 +13,8 @@ import com.sap.sailing.domain.common.TimePoint;
/**
* A simplified implementation of the {@link EventBase} interface which maintains an immutable collection of
* {@link LeaderboardGroupBase} objects to implement the {@link #getLeaderboardGroups()} method.
* {@link LeaderboardGroupBase} objects to implement the {@link #getLeaderboardGroups()} method. A local image
* size cache can be maintained using the {@link #setImageSize} method.
*
* @author Axel Uhl (D043530)
*
@@ -17,21 +22,31 @@ import com.sap.sailing.domain.common.TimePoint;
public class StrippedEventImpl extends EventBaseImpl {
private static final long serialVersionUID = 5608501747499933988L;
private final Iterable<LeaderboardGroupBase> leaderboardGroups;
private final Map<URL, ImageSize> imageSizes;
public StrippedEventImpl(String name, TimePoint startDate, TimePoint endDate, String venueName,
boolean isPublic, UUID id, Iterable<LeaderboardGroupBase> leaderboardGroups) {
super(name, startDate, endDate, venueName, isPublic, id);
this.leaderboardGroups = leaderboardGroups;
this(name, startDate, endDate, new VenueImpl(venueName), isPublic, id, leaderboardGroups);
}
public StrippedEventImpl(String name, TimePoint startDate, TimePoint endDate, Venue venue,
boolean isPublic, UUID id, Iterable<LeaderboardGroupBase> leaderboardGroups) {
super(name, startDate, endDate, venue, isPublic, id);
this.leaderboardGroups = leaderboardGroups;
this.imageSizes = new HashMap<URL, EventBase.ImageSize>();
}
@Override
public Iterable<LeaderboardGroupBase> getLeaderboardGroups() {
return leaderboardGroups;
}
public void setImageSize(URL imageURL, ImageSize imageSize) {
imageSizes.put(imageURL, imageSize);
}
@Override
public ImageSize getImageSize(URL imageURL) throws InterruptedException, ExecutionException {
return imageSizes.get(imageURL);
}
}
@@ -1,6 +1,5 @@
package com.sap.sailing.domain.base;
import java.awt.Dimension;
import java.net.URL;
import java.util.UUID;
import java.util.concurrent.ExecutionException;
@@ -59,6 +58,6 @@ public interface Event extends EventBase {
* Note that exceptions may result should the image be unavailable. If the image URL leads to a document that is not an image,
* <code>null</code> will result.
*/
Dimension getImageSize(URL imageURL) throws InterruptedException, ExecutionException;
ImageSize getImageSize(URL imageURL) throws InterruptedException, ExecutionException;
}
@@ -1,6 +1,5 @@
package com.sap.sailing.domain.base.impl;
import java.awt.Dimension;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.net.URL;
@@ -36,7 +35,7 @@ public class EventImpl extends EventBaseImpl implements Event {
private ConcurrentLinkedQueue<LeaderboardGroup> leaderboardGroups;
private transient ConcurrentHashMap<URL, Future<Dimension>> imageSizeFetchers;
private transient ConcurrentHashMap<URL, Future<ImageSize>> imageSizeFetchers;
private transient ExecutorService executor;
public EventImpl(String name, TimePoint startDate, TimePoint endDate, String venueName, boolean isPublic, UUID id) {
@@ -103,13 +102,13 @@ public class EventImpl extends EventBaseImpl implements Event {
return leaderboardGroups.remove(leaderboardGroup);
}
private Future<Dimension> getOrCreateImageSizeCalculator(final URL imageURL) {
Future<Dimension> imageSizeFetcher = imageSizeFetchers.get(imageURL);
private Future<ImageSize> getOrCreateImageSizeCalculator(final URL imageURL) {
Future<ImageSize> imageSizeFetcher = imageSizeFetchers.get(imageURL);
if (imageSizeFetcher == null) {
imageSizeFetcher = executor.submit(new Callable<Dimension>() {
imageSizeFetcher = executor.submit(new Callable<ImageSize>() {
@Override
public Dimension call() throws IOException {
Dimension result = null;
public ImageSize call() throws IOException {
ImageSize result = null;
ImageInputStream in = null;
try {
URLConnection conn = imageURL.openConnection();
@@ -119,7 +118,7 @@ public class EventImpl extends EventBaseImpl implements Event {
ImageReader reader = readers.next();
try {
reader.setInput(in);
result = new Dimension(reader.getWidth(0), reader.getHeight(0));
result = new ImageSizeImpl(reader.getWidth(0), reader.getHeight(0));
} finally {
reader.dispose();
}
@@ -138,8 +137,8 @@ public class EventImpl extends EventBaseImpl implements Event {
}
@Override
public Dimension getImageSize(URL imageURL) throws InterruptedException, ExecutionException {
Future<Dimension> imageSizeCalculator = getOrCreateImageSizeCalculator(imageURL);
public ImageSize getImageSize(URL imageURL) throws InterruptedException, ExecutionException {
Future<ImageSize> imageSizeCalculator = getOrCreateImageSizeCalculator(imageURL);
return imageSizeCalculator.get();
}
@@ -116,6 +116,8 @@ public class EventBaseDTO extends NamedDTO implements IsSerializable {
public String getStageImageURL() {
String result = null;
for(String imageUrl: imageURLs) {
// FIXME consolidate with getPhotoGalleryImageURLs, removing redundant "STAGE" code
// TODO use image size to find largest image(s) in case none is explicitly marked with "state" in its URL
if(imageUrl.contains("stage") || imageUrl.contains("STAGE")) {
result = imageUrl;
break;
@@ -127,6 +129,7 @@ public class EventBaseDTO extends NamedDTO implements IsSerializable {
public List<String> getPhotoGalleryImageURLs() {
List<String> result = new ArrayList<String>();
for(String imageUrl: imageURLs) {
// FIXME consolidate with getStageImageURL, removing redundant "STAGE" code
if(!imageUrl.contains("stage") && !imageUrl.contains("STAGE")) {
result.add(imageUrl);
}
@@ -50,7 +50,7 @@ public class EventBaseJsonDeserializer implements JsonDeserializer<EventBase> {
leaderboardGroups.add(leaderboardGroupDeserializer.deserialize((JSONObject) lgJson));
}
}
EventBaseImpl result = new StrippedEventImpl(name, startDate == null ? null : new MillisecondsTimePoint(startDate.longValue()),
StrippedEventImpl result = new StrippedEventImpl(name, startDate == null ? null : new MillisecondsTimePoint(startDate.longValue()),
endDate == null ? null : new MillisecondsTimePoint(endDate.longValue()), venue, /* is public */ true, id, leaderboardGroups);
result.setDescription(description);
if (officialWebsiteURLAsString != null) {
@@ -88,6 +88,21 @@ public class EventBaseJsonDeserializer implements JsonDeserializer<EventBase> {
throw new JsonDeserializationException("Error deserializing sponsor image URLs for event "+name, e);
}
}
JSONArray imageSizes = (JSONArray) object.get(EventBaseJsonSerializer.FIELD_IMAGE_SIZES);
if (imageSizes != null) {
for (Object imageURLAndSizeObject : imageSizes) {
JSONObject imageURLAndSizeJson = (JSONObject) imageURLAndSizeObject;
try {
result.setImageSize(
new URL((String) imageURLAndSizeJson.get(EventBaseJsonSerializer.FIELD_IMAGE_URL)),
new EventBaseImpl.ImageSizeImpl(
((Number) imageURLAndSizeJson.get(EventBaseJsonSerializer.FIELD_IMAGE_WIDTH)).intValue(),
((Number) imageURLAndSizeJson.get(EventBaseJsonSerializer.FIELD_IMAGE_HEIGHT)).intValue()));
} catch (MalformedURLException e) {
throw new JsonDeserializationException(e);
}
}
}
return result;
}
@@ -1,16 +1,22 @@
package com.sap.sailing.server.gateway.serialization.impl;
import java.net.URL;
import java.util.concurrent.ExecutionException;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import com.sap.sailing.domain.base.EventBase;
import com.sap.sailing.domain.base.EventBase.ImageSize;
import com.sap.sailing.domain.base.LeaderboardGroupBase;
import com.sap.sailing.domain.base.Venue;
import com.sap.sailing.server.gateway.serialization.JsonSerializer;
public class EventBaseJsonSerializer implements JsonSerializer<EventBase> {
private static final Logger logger = Logger.getLogger(EventBaseJsonSerializer.class.getName());
public static final String FIELD_ID = "id";
public static final String FIELD_NAME = "name";
public static final String FIELD_DESCRIPTION = "description";
@@ -21,6 +27,10 @@ public class EventBaseJsonSerializer implements JsonSerializer<EventBase> {
public static final String FIELD_VIDEO_URLS = "videoURLs";
public static final String FIELD_SPONSOR_IMAGE_URLS = "sponsorImageURLs";
public static final String FIELD_LOGO_IMAGE_URL = "logoImageURL";
public static final String FIELD_IMAGE_SIZES = "imageSizes";
public static final String FIELD_IMAGE_URL = "imageURL";
public static final String FIELD_IMAGE_WIDTH = "imageWidth";
public static final String FIELD_IMAGE_HEIGHT = "imageHeight";
public static final String FIELD_OFFICIAL_WEBSITE_URL = "officialWebsiteURL";
public static final String FIELDS_LEADERBOARD_GROUPS = "leaderboardGroups";
@@ -50,9 +60,42 @@ public class EventBaseJsonSerializer implements JsonSerializer<EventBase> {
for (LeaderboardGroupBase lg : event.getLeaderboardGroups()) {
leaderboardGroups.add(leaderboardGroupBaseSerializer.serialize(lg));
}
JSONArray imageSizes = new JSONArray();
result.put(FIELD_IMAGE_SIZES, imageSizes);
for (URL imageURL : event.getImageURLs()) {
addImageSize(imageURL, imageSizes, event);
}
if (event.getLogoImageURL() != null) {
addImageSize(event.getLogoImageURL(), imageSizes, event);
}
for (URL sponsorImageURL : event.getSponsorImageURLs()) {
addImageSize(sponsorImageURL, imageSizes, event);
}
return result;
}
/**
* If <code>eventBase</code> knows the size of the image with URL <code>imageURL</code>, that size is serialized as
* JSON object to <code>imageSizes</code>
*/
private void addImageSize(URL imageURL, JSONArray imageSizes, EventBase eventBase) {
ImageSize imageSize;
try {
imageSize = eventBase.getImageSize(imageURL);
if (imageSize != null) {
JSONObject imageSizeJson = new JSONObject();
imageSizes.add(imageSizeJson);
imageSizeJson.put(FIELD_IMAGE_URL, imageURL.toString());
imageSizeJson.put(FIELD_IMAGE_WIDTH, imageSize.getWidth());
imageSizeJson.put(FIELD_IMAGE_HEIGHT, imageSize.getHeight());
}
} catch (InterruptedException e) {
logger.log(Level.FINE, "Couldn't retrieve image size for URL "+imageURL, e);
} catch (ExecutionException e) {
logger.log(Level.FINE, "Couldn't retrieve image size for URL "+imageURL, e);
}
}
private JSONArray getURLsAsStringArray(Iterable<URL> urls) {
JSONArray jsonImageURLs = new JSONArray();
for (URL url : urls) {
@@ -118,7 +118,9 @@ public class SearchServiceTest {
cal.set(2014, 5, 8, 16, 00);
final TimePoint pfingstbuschEndDate = new MillisecondsTimePoint(cal.getTime());
pfingstbusch = server.apply(new CreateEvent("Pfingsbusch", pfingstbuschStartDate, pfingstbuschEndDate, "Kiel", /* isPublic */
true, UUID.randomUUID(), Collections.<URL>emptySet(), Collections.<URL>emptySet(), createURLsFromString(sponsorImageURLs), (logoimageURL)AsString == null ? null : new URL(logoimageURLAsString), officialWebsiteURLAsString == null ? null : new URL(officialWebsiteURLAsString)));
true, UUID.randomUUID(), Collections.<URL>emptySet(), Collections.<URL>emptySet(),
/* sponsorImageURLs */ Collections.<URL>emptySet(),
/* logoImageURLAsString */ null, /* officialWebsiteURLAsString */ null));
kiel = pfingstbusch.getVenue();
final CourseAreaImpl kielAlpha = new CourseAreaImpl("Alpha", UUID.randomUUID());
kiel.addCourseArea(kielAlpha);
@@ -149,7 +151,9 @@ public class SearchServiceTest {
cal.set(2014, 5, 8, 18, 00);
final TimePoint aalEndDate = new MillisecondsTimePoint(cal.getTime());
aalEvent = server.apply(new CreateEvent("Aalregatta", aalStartDate, aalEndDate, "Flensburg", /* isPublic */
true, UUID.randomUUID(), Collections.<URL>emptySet(), Collections.<URL>emptySet(), createURLsFromString(sponsorImageURLs), (logoimageURL)AsString == null ? null : new URL(logoimageURLAsString), officialWebsiteURLAsString == null ? null : new URL(officialWebsiteURLAsString)));
true, UUID.randomUUID(), Collections.<URL>emptySet(), Collections.<URL>emptySet(),
/* sponsorImageURLs */ Collections.<URL>emptySet(), /* logoimageURL */ null,
/* officialWebsiteURLAsString */ null));
flensburg = aalEvent.getVenue();
final CourseAreaImpl flensburgStandard = new CourseAreaImpl("Standard", UUID.randomUUID());
flensburg.addCourseArea(flensburgStandard);