package edu.pdx.cs410J.rmi;
import java.net.MalformedURLException;
import java.rmi.Naming;
import java.rmi.RMISecurityManager;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.util.*;
import java.util.stream.Collectors;
/**
* This class provides an implementation of the remote {@link
* MovieDatabase} interface. Note that this class does not extened
* {@link UnicastRemoteObject}. Therefore, we have to invoke {@link
* UnicastRemoteObject#exportObject} in the constructor.
*/
public class MovieDatabaseImpl implements MovieDatabase {
/** A map that maps id's to their movies. This allows for a
* fast lookup of movies by their id. */
private Map<Long, Movie> movies;
//////////////////////// Constructors /////////////////////////
/**
* Creates a new <code>MovieDatabaseImpl</code>.
* @throws java.rmi.RemoteException
*/
public MovieDatabaseImpl() throws RemoteException {
// Sort movies by their id, so the lookup is O(lg n)
this.movies = new TreeMap<>(Long::compareTo);
System.out.println("Starting Movie Database");
UnicastRemoteObject.exportObject(this);
}
/////////////////////// Remote Methods ////////////////////////
/**
* Creates a new <code>Movie</code> object on the server. It
* returns the id of the movie that was created.
*
* @param title
* The title of the movie
* @param year
* The year in which the movie was released
*/
@Override
public long createMovie(String title, int year)
throws RemoteException {
Movie movie = new Movie(title, year);
long id = movie.getId();
this.movies.put(id, movie);
System.out.println("Created a new movie " + movie);
return id;
}
/**
* Returns the <code>Movie</code> with the given id.
*/
@Override
public Movie getMovie(long id) throws RemoteException {
return this.movies.get(id);
}
/**
* Makes note of a character in a given movie played by a given
* actor.
*
* @throws IllegalArgumentException
* There is no movie with <code>movieId</code> or the
* character is already played by someone else
*/
@Override
public void noteCharacter(long movieId, String character, long actorId)
throws RemoteException {
Movie movie = getExistingMovie(movieId);
movie.addCharacter(character, actorId);
}
private Movie getExistingMovie(long movieId) throws RemoteException {
// Note local call of remote method
Movie movie = this.getMovie(movieId);
if (movie == null) {
String s = "There is no movie with id " + movieId;
throw new IllegalArgumentException(s);
}
return movie;
}
/**
* Returns the movie in which a given actor acted. The movies are
* sorted by release date.
*/
@Override
public SortedSet<Movie> getFilmography(final long actorId)
throws RemoteException {
Query query = movie -> movie.getActors().contains(actorId);
Comparator<Movie> sorter = new SortMoviesByReleaseDate();
return executeQuery(query, sorter);
}
/**
* A comparator that sorts movies based on the year in which they
* were released. It must be serializable so that it may be sent to
* the client. It must be static so that it doesn't have a
* reference to its outer class.
*/
static class SortMoviesByReleaseDate
implements Comparator<Movie>, java.io.Serializable {
@Override
public int compare(Movie movie1, Movie movie2) {
int year1 = movie1.getYear();
int year2 = movie2.getYear();
return year1 > year2 ? 1 : year1 < year2 ? -1 : 0;
}
}
/**
* Performs a query on the database. The movies that match the
* query are sorted using the given comparator.
*/
@Override
public SortedSet<Movie> executeQuery(Query query, Comparator<Movie> sorter)
throws RemoteException {
return this.movies.values().stream()
.filter(query::satisfies)
.collect(Collectors.toCollection(() -> new TreeSet<>(sorter)));
}
/**
* Unregisters this <code>MovieDatabaseImpl</code> with the RMI
* registry.
*/
@Override
public void shutdown() throws RemoteException {
System.out.println("Shutting down Movie Database");
UnicastRemoteObject.unexportObject(this, false /* force */);
System.exit(0);
}
@Override
public Collection<Movie> getMovies() throws RemoteException {
return this.movies.values();
}
@Override
public void deleteMovie(long movieId) throws RemoteException {
Movie movie = getExistingMovie(movieId);
this.movies.remove(movie.getId());
}
/////////////////////// Main Program /////////////////////////
/**
* This main program registers an instance of MovieDatabaseImpl in
* an RMI registry.
*/
public static void main(String[] args) {
String host = args[0];
int port = Integer.parseInt(args[1]);
// Install an RMISecurityManager, if there is not a
// SecurityManager already installed
if (System.getSecurityManager() == null) {
System.setSecurityManager(new RMISecurityManager());
}
String name = "rmi://" + host + ":" + port + "/MovieDatabase";
try {
MovieDatabase db = new MovieDatabaseImpl();
Naming.rebind(name, db);
} catch (RemoteException | MalformedURLException ex) {
ex.printStackTrace(System.err);
}
}
}