import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Predicates.not;
+import static net.pterodactylus.sone.data.Sone.LOCAL_SONE_FILTER;
import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.logging.Logger;
import net.pterodactylus.sone.core.Options.DefaultOption;
-import net.pterodactylus.sone.core.Options.Option;
-import net.pterodactylus.sone.core.Options.OptionWatcher;
+import net.pterodactylus.sone.core.SoneInserter.SetInsertionDelay;
import net.pterodactylus.sone.core.event.ImageInsertFinishedEvent;
import net.pterodactylus.sone.core.event.MarkPostKnownEvent;
import net.pterodactylus.sone.core.event.MarkPostReplyKnownEvent;
import net.pterodactylus.sone.core.event.SoneRemovedEvent;
import net.pterodactylus.sone.core.event.SoneUnlockedEvent;
import net.pterodactylus.sone.data.Album;
-import net.pterodactylus.sone.data.AlbumImpl;
import net.pterodactylus.sone.data.Client;
import net.pterodactylus.sone.data.Image;
import net.pterodactylus.sone.data.Post;
import net.pterodactylus.sone.data.Sone;
import net.pterodactylus.sone.data.Sone.ShowCustomAvatars;
import net.pterodactylus.sone.data.Sone.SoneStatus;
+import net.pterodactylus.sone.data.SoneImpl;
import net.pterodactylus.sone.data.TemporaryImage;
import net.pterodactylus.sone.database.Database;
import net.pterodactylus.sone.database.DatabaseException;
import net.pterodactylus.sone.database.PostReplyProvider;
import net.pterodactylus.sone.database.SoneProvider;
import net.pterodactylus.sone.fcp.FcpInterface;
-import net.pterodactylus.sone.fcp.FcpInterface.FullAccessRequired;
import net.pterodactylus.sone.freenet.wot.Identity;
import net.pterodactylus.sone.freenet.wot.IdentityManager;
import net.pterodactylus.sone.freenet.wot.OwnIdentity;
import net.pterodactylus.util.service.AbstractService;
import net.pterodactylus.util.thread.NamedThreadFactory;
+import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Optional;
-import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.HashMultimap;
private final EventBus eventBus;
/** The configuration. */
- private Configuration configuration;
+ private final Configuration configuration;
/** Whether we’re currently saving the configuration. */
private boolean storingConfiguration = false;
/** Trusted identities, sorted by own identities. */
private final Multimap<OwnIdentity, Identity> trustedIdentities = Multimaps.synchronizedSetMultimap(HashMultimap.<OwnIdentity, Identity>create());
- /** All known albums. */
- private final Map<String, Album> albums = new HashMap<String, Album>();
-
- /** All known images. */
- private final Map<String, Image> images = new HashMap<String, Image>();
-
/** All temporary images. */
private final Map<String, TemporaryImage> temporaryImages = new HashMap<String, TemporaryImage>();
}
/**
- * Sets the configuration to use. This will automatically save the current
- * configuration to the given configuration.
- *
- * @param configuration
- * The new configuration to use
- */
- public void setConfiguration(Configuration configuration) {
- this.configuration = configuration;
- touchConfiguration();
- }
-
- /**
* Returns the options used by the core.
*
* @return The options of the core
@Override
public Collection<Sone> getLocalSones() {
synchronized (sones) {
- return FluentIterable.from(sones.values()).filter(new Predicate<Sone>() {
-
- @Override
- public boolean apply(Sone sone) {
- return sone.isLocal();
- }
- }).toSet();
+ return FluentIterable.from(sones.values()).filter(LOCAL_SONE_FILTER).toSet();
}
}
synchronized (sones) {
Sone sone = sones.get(id);
if ((sone == null) && create) {
- sone = new Sone(id, true);
+ sone = new SoneImpl(id, true);
sones.put(id, sone);
}
if ((sone != null) && !sone.isLocal()) {
- sone = new Sone(id, true);
+ sone = new SoneImpl(id, true);
sones.put(id, sone);
}
return sone;
@Override
public Collection<Sone> getRemoteSones() {
synchronized (sones) {
- return FluentIterable.from(sones.values()).filter(new Predicate<Sone>() {
-
- @Override
- public boolean apply(Sone sone) {
- return !sone.isLocal();
- }
- }).toSet();
+ return FluentIterable.from(sones.values()).filter(not(LOCAL_SONE_FILTER)).toSet();
}
}
synchronized (sones) {
Sone sone = sones.get(id);
if ((sone == null) && create && (id != null) && (id.length() == 43)) {
- sone = new Sone(id, false);
+ sone = new SoneImpl(id, false);
sones.put(id, sone);
}
return sone;
* {@code false} otherwise
*/
public boolean isModifiedSone(Sone sone) {
- return (soneInserters.containsKey(sone)) ? soneInserters.get(sone).isModified() : false;
+ return soneInserters.containsKey(sone) && soneInserters.get(sone).isModified();
}
/**
* given ID exists and {@code create} is {@code false}
*/
public Album getAlbum(String albumId, boolean create) {
- synchronized (albums) {
- Album album = albums.get(albumId);
- if (create && (album == null)) {
- album = new AlbumImpl(albumId);
- albums.put(albumId, album);
- }
- return album;
+ Optional<Album> album = database.getAlbum(albumId);
+ if (album.isPresent()) {
+ return album.get();
}
+ if (!create) {
+ return null;
+ }
+ Album newAlbum = database.newAlbumBuilder().withId(albumId).build();
+ database.storeAlbum(newAlbum);
+ return newAlbum;
}
/**
* none was created
*/
public Image getImage(String imageId, boolean create) {
- synchronized (images) {
- Image image = images.get(imageId);
- if (create && (image == null)) {
- image = new Image(imageId);
- images.put(imageId, image);
- }
- return image;
+ Optional<Image> image = database.getImage(imageId);
+ if (image.isPresent()) {
+ return image.get();
+ }
+ if (!create) {
+ return null;
}
+ Image newImage = database.newImageBuilder().withId(imageId).build();
+ database.storeImage(newImage);
+ return newImage;
}
/**
}
}
soneDownloader.addSone(sone);
- soneDownloaders.execute(new Runnable() {
-
- @Override
- @SuppressWarnings("synthetic-access")
- public void run() {
- soneDownloader.fetchSone(sone, sone.getRequestUri());
- }
-
- });
+ soneDownloaders.execute(soneDownloader.new FetchSone(sone));
return sone;
}
}
return;
}
/* find removed posts. */
+ Collection<Post> removedPosts = new ArrayList<Post>();
+ Collection<Post> newPosts = new ArrayList<Post>();
Collection<Post> existingPosts = database.getPosts(sone.getId());
for (Post oldPost : existingPosts) {
if (!sone.getPosts().contains(oldPost)) {
- eventBus.post(new PostRemovedEvent(oldPost));
+ removedPosts.add(oldPost);
}
}
/* find new posts. */
if (newPost.getTime() < getSoneFollowingTime(sone)) {
newPost.setKnown(true);
} else if (!newPost.isKnown()) {
- eventBus.post(new NewPostFoundEvent(newPost));
+ newPosts.add(newPost);
}
}
/* store posts. */
database.storePosts(sone, sone.getPosts());
+ Collection<PostReply> newPostReplies = new ArrayList<PostReply>();
+ Collection<PostReply> removedPostReplies = new ArrayList<PostReply>();
if (!soneRescueMode) {
for (PostReply reply : storedSone.get().getReplies()) {
if (!sone.getReplies().contains(reply)) {
- eventBus.post(new PostReplyRemovedEvent(reply));
+ removedPostReplies.add(reply);
}
}
}
if (reply.getTime() < getSoneFollowingTime(sone)) {
reply.setKnown(true);
} else if (!reply.isKnown()) {
- eventBus.post(new NewPostReplyFoundEvent(reply));
+ newPostReplies.add(reply);
}
}
database.storePostReplies(sone, sone.getReplies());
- synchronized (albums) {
- synchronized (images) {
- for (Album album : storedSone.get().getRootAlbum().getAlbums()) {
- albums.remove(album.getId());
- for (Image image : album.getImages()) {
- images.remove(image.getId());
- }
- }
- for (Album album : sone.getRootAlbum().getAlbums()) {
- albums.put(album.getId(), album);
- for (Image image : album.getImages()) {
- images.put(image.getId(), image);
- }
- }
+ for (Album album : storedSone.get().getRootAlbum().getAlbums()) {
+ database.removeAlbum(album);
+ for (Image image : album.getImages()) {
+ database.removeImage(image);
+ }
+ }
+ for (Post removedPost : removedPosts) {
+ eventBus.post(new PostRemovedEvent(removedPost));
+ }
+ for (Post newPost : newPosts) {
+ eventBus.post(new NewPostFoundEvent(newPost));
+ }
+ for (PostReply removedPostReply : removedPostReplies) {
+ eventBus.post(new PostReplyRemovedEvent(removedPostReply));
+ }
+ for (PostReply newPostReply : newPostReplies) {
+ eventBus.post(new NewPostReplyFoundEvent(newPostReply));
+ }
+ for (Album album : sone.getRootAlbum().getAlbums()) {
+ database.storeAlbum(album);
+ for (Image image : album.getImages()) {
+ database.storeImage(image);
}
}
synchronized (sones) {
logger.log(Level.WARNING, "Invalid album found, aborting load!");
return;
}
- Album album = getAlbum(albumId).setSone(sone).setTitle(albumTitle).setDescription(albumDescription).setAlbumImage(albumImageId);
+ Album album = getAlbum(albumId).setSone(sone).modify().setTitle(albumTitle).setDescription(albumDescription).setAlbumImage(albumImageId).update();
if (albumParentId != null) {
Album parentAlbum = getAlbum(albumParentId, false);
if (parentAlbum == null) {
logger.log(Level.WARNING, "Invalid album image encountered, aborting load!");
return;
}
- Image image = getImage(imageId).setSone(sone).setCreationTime(creationTime).setKey(key);
- image.setTitle(title).setDescription(description).setWidth(width).setHeight(height);
+ Image image = getImage(imageId).modify().setSone(sone).setCreationTime(creationTime).setKey(key).setTitle(title).setDescription(description).setWidth(width).setHeight(height).update();
album.addImage(image);
}
eventBus.post(new NewPostFoundEvent(post));
sone.addPost(post);
touchConfiguration();
- localElementTicker.schedule(new Runnable() {
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void run() {
- markPostKnown(post);
- }
- }, 10, TimeUnit.SECONDS);
+ localElementTicker.schedule(new MarkPostKnown(post), 10, TimeUnit.SECONDS);
return post;
}
eventBus.post(new NewPostReplyFoundEvent(reply));
sone.addReply(reply);
touchConfiguration();
- localElementTicker.schedule(new Runnable() {
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void run() {
- markReplyKnown(reply);
- }
- }, 10, TimeUnit.SECONDS);
+ localElementTicker.schedule(new MarkReplyKnown(reply), 10, TimeUnit.SECONDS);
return reply;
}
* @return The new album
*/
public Album createAlbum(Sone sone, Album parent) {
- AlbumImpl album = new AlbumImpl();
- synchronized (albums) {
- albums.put(album.getId(), album);
- }
+ Album album = database.newAlbumBuilder().randomId().build();
+ database.storeAlbum(album);
album.setSone(sone);
parent.addAlbum(album);
return album;
return;
}
album.getParent().removeAlbum(album);
- synchronized (albums) {
- albums.remove(album.getId());
- }
+ database.removeAlbum(album);
touchConfiguration();
}
checkNotNull(temporaryImage, "temporaryImage must not be null");
checkArgument(sone.isLocal(), "sone must be a local Sone");
checkArgument(sone.equals(album.getSone()), "album must belong to the given Sone");
- Image image = new Image(temporaryImage.getId()).setSone(sone).setCreationTime(System.currentTimeMillis());
+ Image image = database.newImageBuilder().withId(temporaryImage.getId()).build().modify().setSone(sone).setCreationTime(System.currentTimeMillis()).update();
album.addImage(image);
- synchronized (images) {
- images.put(image.getId(), image);
- }
+ database.storeImage(image);
imageInserter.insertImage(temporaryImage, image);
return image;
}
checkArgument(image.getSone().isLocal(), "image must belong to a local Sone");
deleteTemporaryImage(image.getId());
image.getAlbum().removeImage(image);
- synchronized (images) {
- images.remove(image.getId());
- }
+ database.removeImage(image);
touchConfiguration();
}
synchronized (sones) {
for (Entry<Sone, SoneInserter> soneInserter : soneInserters.entrySet()) {
soneInserter.getValue().stop();
- saveSone(soneInserter.getKey());
+ saveSone(getLocalSone(soneInserter.getKey().getId(), false));
}
}
saveConfiguration();
*/
private void loadConfiguration() {
/* create options. */
- options.addIntegerOption("InsertionDelay", new DefaultOption<Integer>(60, new IntegerRangePredicate(0, Integer.MAX_VALUE), new OptionWatcher<Integer>() {
-
- @Override
- public void optionChanged(Option<Integer> option, Integer oldValue, Integer newValue) {
- SoneInserter.setInsertionDelay(newValue);
- }
-
- }));
+ options.addIntegerOption("InsertionDelay", new DefaultOption<Integer>(60, new IntegerRangePredicate(0, Integer.MAX_VALUE), new SetInsertionDelay()));
options.addIntegerOption("PostsPerPage", new DefaultOption<Integer>(10, new IntegerRangePredicate(1, Integer.MAX_VALUE)));
options.addIntegerOption("ImagesPerPage", new DefaultOption<Integer>(9, new IntegerRangePredicate(1, Integer.MAX_VALUE)));
options.addIntegerOption("CharactersPerPost", new DefaultOption<Integer>(400, Predicates.<Integer> or(new IntegerRangePredicate(50, Integer.MAX_VALUE), Predicates.equalTo(-1))));
options.addIntegerOption("PositiveTrust", new DefaultOption<Integer>(75, new IntegerRangePredicate(0, 100)));
options.addIntegerOption("NegativeTrust", new DefaultOption<Integer>(-25, new IntegerRangePredicate(-100, 100)));
options.addStringOption("TrustComment", new DefaultOption<String>("Set from Sone Web Interface"));
- options.addBooleanOption("ActivateFcpInterface", new DefaultOption<Boolean>(false, new OptionWatcher<Boolean>() {
-
- @Override
- @SuppressWarnings("synthetic-access")
- public void optionChanged(Option<Boolean> option, Boolean oldValue, Boolean newValue) {
- fcpInterface.setActive(newValue);
- }
- }));
- options.addIntegerOption("FcpFullAccessRequired", new DefaultOption<Integer>(2, new OptionWatcher<Integer>() {
-
- @Override
- @SuppressWarnings("synthetic-access")
- public void optionChanged(Option<Integer> option, Integer oldValue, Integer newValue) {
- fcpInterface.setFullAccessRequired(FullAccessRequired.values()[newValue]);
- }
-
- }));
+ options.addBooleanOption("ActivateFcpInterface", new DefaultOption<Boolean>(false, fcpInterface.new SetActive()));
+ options.addIntegerOption("FcpFullAccessRequired", new DefaultOption<Integer>(2, fcpInterface.new SetFullAccessRequired()));
loadConfigurationValue("InsertionDelay");
loadConfigurationValue("PostsPerPage");
@Subscribe
public void imageInsertFinished(ImageInsertFinishedEvent imageInsertFinishedEvent) {
logger.log(Level.WARNING, String.format("Image insert finished for %s: %s", imageInsertFinishedEvent.image(), imageInsertFinishedEvent.resultingUri()));
- imageInsertFinishedEvent.image().setKey(imageInsertFinishedEvent.resultingUri().toString());
+ imageInsertFinishedEvent.image().modify().setKey(imageInsertFinishedEvent.resultingUri().toString()).update();
deleteTemporaryImage(imageInsertFinishedEvent.image().getId());
touchConfiguration();
}
+ @VisibleForTesting
+ class MarkPostKnown implements Runnable {
+
+ private final Post post;
+
+ public MarkPostKnown(Post post) {
+ this.post = post;
+ }
+
+ @Override
+ public void run() {
+ markPostKnown(post);
+ }
+
+ }
+
+ @VisibleForTesting
+ class MarkReplyKnown implements Runnable {
+
+ private final PostReply postReply;
+
+ public MarkReplyKnown(PostReply postReply) {
+ this.postReply = postReply;
+ }
+
+ @Override
+ public void run() {
+ markReplyKnown(postReply);
+ }
+
+ }
+
}