package no.java.incogito.application; import fj.Bottom; import fj.Effect; import fj.F; import fj.F2; import static fj.Function.curry; import fj.P; import fj.P1; import fj.P2; import fj.Unit; import fj.control.parallel.Callables; import fj.data.Either; import static fj.data.Either.joinRight; import static fj.data.Either.rights; import fj.data.List; import fj.data.Option; import fj.data.TreeMap; import no.java.ems.domain.Binary; import no.java.ems.domain.Speaker; import static no.java.incogito.Functions.compose; import static no.java.incogito.application.EmsFunctions.eventFromEms; import static no.java.incogito.application.EmsFunctions.sessionFromEms; import no.java.incogito.application.IncogitoConfiguration.EventConfiguration; import static no.java.incogito.application.IncogitoConfiguration.unconfigured; import static no.java.incogito.application.OperationResult.notFound; import static no.java.incogito.application.OperationResult.ok; import no.java.incogito.domain.Event; import no.java.incogito.domain.Label; import no.java.incogito.domain.Level; import no.java.incogito.domain.Level.LevelId; import no.java.incogito.domain.Schedule; import no.java.incogito.domain.Session; import no.java.incogito.domain.SessionId; import no.java.incogito.domain.User; import no.java.incogito.domain.User.UserId; import no.java.incogito.domain.UserSessionAssociation.InterestLevel; import no.java.incogito.ems.client.EmsWrapper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.stereotype.Component; import java.io.File; /** * @author <a href="mailto:trygvis@java.no">Trygve Laugstøl</a> * @version $Id$ */ @Component("incogitoApplication") public class DefaultIncogitoApplication implements IncogitoApplication, InitializingBean { private final Logger logger = LoggerFactory.getLogger(this.getClass()); private final File incogitoHome; private final UserClient userClient; private final EmsWrapper emsWrapper; /** * This is not ideal, but a configuration is required to load the configuration */ private IncogitoConfiguration configuration = unconfigured; @Autowired public DefaultIncogitoApplication(@Qualifier("incogitoHome") File incogitoHome, UserClient userClient, EmsWrapper emsWrapper) { this.incogitoHome = incogitoHome; this.userClient = userClient; this.emsWrapper = emsWrapper; } public void afterPropertiesSet() throws Exception { reloadConfiguration(); logger.info("Configuration: "); logger.info("Incogito Home: " + incogitoHome); OperationResult<List<Event>> result = getEvents(); if (!result.isOk()) { logger.info("No events configured."); return; } List<Event> events = result.value(); for (Event event : events) { logger.info("Event: " + event.name); logger.info(" Labels: (" + event.labels.length() + ")"); for (Label label : event.labels) { logger.info(" " + label.displayName + " (" + label.id + "=>" + label.emsId + "), icon: " + label.iconFile.getName()); } TreeMap<LevelId, Level> levels = event.levels; logger.info(" Levels: (" + levels.size() + ")"); for (P2<LevelId, Level> level : levels) { logger.info(" " + level._2().displayName + " (" + level._2().id + "), icon: " + level._2().iconFile.getName()); } } } // ----------------------------------------------------------------------- // IncogitoApplication Implementation // ----------------------------------------------------------------------- public IncogitoConfiguration getConfiguration() { return configuration; } /* * TODO: Consider improving the logging here. * Set the initial logger to log to debug() and log only any changes to info(). That way there will be no repeated * logging output, but it will be possible to turn it on to check that the application is working. */ public void reloadConfiguration() throws Exception { this.configuration = new ConfigurationLoaderService(emsWrapper).loadConfiguration(incogitoHome, configuration); } public OperationResult<List<Event>> getEvents() { List<Either<String, no.java.ems.domain.Event>> emsEvents = getConfiguration().eventConfigurations. map(compose(emsWrapper.findEventByName, EventConfiguration.name_)); List<Either<String, Event>> events = Either.<String, no.java.ems.domain.Event>rights(emsEvents). map(eventFromEms.f(configuration)); return ok(rights(events)); } public OperationResult<Event> getEventByName(String eventName) { Either<String, Event> eventOption = emsWrapper. findEventByName.f(eventName). right().bind(eventFromEms.f(configuration)); return eventOption.either(OperationResult.<Event>notFound_(), OperationResult.<Event>ok_()); } public OperationResult<List<Session>> getSessions(String eventName) { return emsWrapper.findEventByName.f(eventName).right().bind(eventFromEms.f(configuration)). either(OperationResult.<List<Session>>notFound_(), compose(OperationResult.<List<Session>>ok_(), getSessionsForEvent)); } public OperationResult<Session> getSessionByTitle(final String eventName, final String sessionTitle) { OperationResult<Event> event = getEventByName(eventName); if (!event.isOk()) { return notFound("Event '" + eventName + "' not found."); } return OperationResult.joinOk(event.ok().map(new F<Event, OperationResult<Session>>() { public OperationResult<Session> f(Event event) { return emsWrapper. findSessionIdsByEventIdAndTitle.f(event.id.toString()).f(sessionTitle).toEither(P.p("Could not find session with title '" + sessionTitle + "'.")). right().bind(sessionFromEms.f(event)). right().map(OperationResult.<Session>ok_()). right().orValue(OperationResult.<Session>$notFound("Could not find session with title '" + sessionTitle + "' not found.")); } })); } public OperationResult<Session> getSession(String eventName, final SessionId sessionId) { Either<String, Session> x = joinRight(emsWrapper.findEventByName.f(eventName).right().bind(eventFromEms.f(configuration)). right().map(new F<Event, Either<String, Session>>() { public Either<String, Session> f(Event event) { return emsWrapper.getSessionById.f(sessionId.value). right().bind(sessionFromEms.f(event)); } })); return OperationResult.fromEither(x); } public OperationResult<User> createUser(User user) { Either<User, User> either = userClient.getUser(user.id).toEither(user); either.left().foreach(new Effect<User>() { public void e(User user) { userClient.setUser(user); } }); return either.either(OperationResult.<User>ok_(), OperationResult.<User>conflict_("User with id '" + user.id + "' already exist.")); } public OperationResult<Unit> removeUser(UserId userId) { if (userClient.removeUser(userId)) { return OperationResult.emptyOk(); } else { return notFound("User with id '" + userId + "' not found."); } } public OperationResult<User> getUser(UserId userId) { Option<User> user = userClient.getUser(userId); if(user.isSome()) { return ok(user.some()); } // TODO: Fetch the user's profile from the central DB User u = User.createPristineUser(userId); userClient.setUser(u); return ok(u); } public OperationResult<Schedule> getSchedule(String eventName, String userId) { Option<User> user = userClient.getUser(new UserId(userId)); if (user.isNone()) { return notFound("User with id '" + userId + "' does not exist."); } return getSchedule_(eventName, user); } public OperationResult<Schedule> getSchedule(String eventName, Option<String> userId) { return getSchedule_(eventName, userId.map(UserId.userId).bind(userClient.getUser)); } private OperationResult<Schedule> getSchedule_(String eventName, final Option<User> user) { return OperationResult.fromEither(emsWrapper.findEventByName.f(eventName).right().bind(eventFromEms.f(configuration)). right().map(createSchedule.f(user))); } public OperationResult<User> setInterestLevel(String userName, String eventName, SessionId sessionId, InterestLevel interestLevel) { // TODO: This is the way it should be // OperationResult<User> result = OperationResult.ok(userClient.getUser(userId)). // ok().map(updateInterestLevelOnUser.f(userSessionAssociation)); // // result.foreach(userClient.setUser); // // return result.map(OperationResult.<User>ok_()). // orSome(OperationResult.<User>$notFound("User '" + userId.value + "' not found.")); Option<User> user = userClient.getUser(new UserId(userName)). map(User.setInterestLevel.f(sessionId).f(interestLevel)); // TODO: Only save the user if needed user.foreach(userClient.setUser); return user.map(OperationResult.<User>ok_()). orSome(OperationResult.<User>$notFound("User '" + userName + "' not found.")); } public OperationResult<byte[]> getSpeakerPhotoForSession(String sessionId, int index) { Either<String, no.java.ems.domain.Session> sessionOption = emsWrapper.getSessionById.f(sessionId); if(sessionOption.isLeft()) { return notFound(sessionOption.left().value()); } no.java.ems.domain.Session session = sessionOption.right().value(); Either<String, Speaker> speakerEither = EmsWrapper.getSpeakerFromSession.f(index).f(session); if(speakerEither.isLeft()) { return notFound(speakerEither.left().value()); } Speaker speaker = speakerEither.right().value(); Either<String, Binary> binary = EmsWrapper.getPhotoFromSpeaker.f(speaker); if (binary.isLeft()) { binary = emsWrapper.getPersonPhoto.f(speaker.getPersonId()); } F<Either<Exception, byte[]>, Either<String, byte[]>> toString = Either.<Exception, byte[], String>leftMap_().f(Bottom.<Exception>eMessage()); Either<String, byte[]> result = joinRight(binary.right().map(compose(toString, P1.<Either<Exception, byte[]>>__1(), Callables.<byte[]>either(), EmsWrapper.fetchBinary))); return result.either(OperationResult.<byte[]>notFound_(), OperationResult.<byte[]>ok_()); } // ----------------------------------------------------------------------- // // ----------------------------------------------------------------------- F<Event, List<Session>> getSessionsForEvent = new F<Event, List<Session>>() { public List<Session> f(Event event) { return Either.rights(emsWrapper.findSessionsByEventId.f(event.id.toString()). map(sessionFromEms.f(event))); } }; F<Option<User>, F<Event, Schedule>> createSchedule = curry(new F2<Option<User>, Event, Schedule>() { public Schedule f(Option<User> userOption, Event event) { return new Schedule(event, getSessionsForEvent.f(event), userOption.map(User.sessionAssociations_).orSome(User.emptySessionAssociations)); } }); }