package org.optaconf.service; import java.util.List; import javax.annotation.Resource; import javax.enterprise.concurrent.ManagedExecutorService; import javax.inject.Inject; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import javax.persistence.TypedQuery; import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.Root; import javax.transaction.HeuristicMixedException; import javax.transaction.HeuristicRollbackException; import javax.transaction.NotSupportedException; import javax.transaction.RollbackException; import javax.transaction.SystemException; import javax.transaction.UserTransaction; import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.PUT; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import org.optaconf.bridge.devoxx.DevoxxImporter; import org.optaconf.domain.Conference; import org.optaplanner.core.api.solver.Solver; import org.optaplanner.core.api.solver.SolverFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @Path("/conference") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) public class ConferenceService { private static final Logger LOG = LoggerFactory.getLogger(ConferenceService.class); @PersistenceContext(unitName = "optaconf-webapp-persistence-unit") private EntityManager em; @Inject private UserTransaction utx; @Inject private DevoxxImporter devoxxImporter; @Inject private SolverFactory<Conference> solverFactory; @Resource(name = "DefaultManagedExecutorService") private ManagedExecutorService executor; private Solver<Conference> solver; @POST @Path("/import/devoxx") public Response importDevoxx() { StringBuilder message = new StringBuilder(); try { utx.begin(); em.joinTransaction(); Conference conference = devoxxImporter.importConference(true); message.append("Devoxx conference with ") .append(conference.getDayList().size()).append(" days, ") .append(conference.getTimeslotList().size()) .append(" timeslots, ").append(conference.getRoomList().size()) .append(" rooms, ").append(conference.getTrackList().size()) .append(" tracks, ").append(conference.getSpeakerList().size()) .append(" speakers, ").append(conference.getTalkList().size()) .append(" talks imported successfully."); LOG.info(message.toString()); } catch (NotSupportedException | SystemException e) { LOG.error(e.getLocalizedMessage(), e); } finally { try { utx.commit(); } catch (SecurityException | IllegalStateException | RollbackException | HeuristicMixedException | HeuristicRollbackException | SystemException e) { LOG.error(e.getLocalizedMessage(), e); } } return Response.ok(message).build(); } @GET @Path("") public List<Conference> getAll() { CriteriaBuilder cb = em.getCriteriaBuilder(); CriteriaQuery<Conference> cq = cb.createQuery(Conference.class); Root<Conference> rootEntry = cq.from(Conference.class); CriteriaQuery<Conference> all = cq.select(rootEntry); TypedQuery<Conference> allQuery = em.createQuery(all); List<Conference> conferences = allQuery.getResultList(); return conferences; } @GET @Path("/{conferenceId}") public Conference getOne(@PathParam("conferenceId") Long conferenceId) { Conference conference = em.find(Conference.class, conferenceId); return conference; } @DELETE @Path("/{conferenceId}") public void deleteOne(@PathParam("conferenceId") Long conferenceId) { // TODO: take care of orphaned entries em.remove(em.find(Conference.class, conferenceId)); } @PUT @Path("/{conferenceId}/solve") public Response solveSchedule(@PathParam("conferenceId") Long conferenceId) { Conference conference = em.find(Conference.class, conferenceId); Solver oldSolver = solver; if (oldSolver != null && oldSolver.isSolving()) { oldSolver.terminateEarly(); } Solver<Conference> solver = solverFactory.buildSolver(); // TODO Use async solving https://developer.jboss.org/message/910391 // executor.submit(new SolverCallable(solver, // scheduleManager.getSchedule())); // return "Solving started."; solver.solve(conference); conference = solver.getBestSolution(); try { utx.begin(); em.joinTransaction(); em.merge(conference); } catch (NotSupportedException | SystemException e) { LOG.error(e.getLocalizedMessage(), e); } finally { try { utx.commit(); } catch (SecurityException | IllegalStateException | RollbackException | HeuristicMixedException | HeuristicRollbackException | SystemException e) { LOG.error(e.getLocalizedMessage(), e); } } return Response.ok(conference).build(); } @GET @Path("/{conferenceId}/isSolving") public Response isSolving(@PathParam("conferenceId") Long conferenceId) { return Response.ok(solver != null && solver.isSolving()).build(); } @PUT @Path("/{conferenceId}/terminateSolving") public Response terminateSolving(@PathParam("conferenceId") Long conferenceId) { if (solver != null) { solver.terminateEarly(); return Response.ok("Solving terminated!").build(); } return Response.status(Response.Status.NOT_FOUND).entity("Solver not found.").build(); } /* private class SolverCallable implements Runnable { private final Solver solver; private final Schedule schedule; private SolverCallable(Solver solver, Schedule schedule) { this.solver = solver; this.schedule = schedule; } public void run() { solver.addEventListener(new SolverEventListener() { @Override public void bestSolutionChanged( BestSolutionChangedEvent bestSolutionChangedEvent) { scheduleManager .setSchedule((Schedule) bestSolutionChangedEvent .getNewBestSolution()); // TODO throws eaten // Exception } }); solver.solve(schedule); Schedule bestSchedule = (Schedule) solver.getBestSolution(); // TODO // throws // eaten // Exception scheduleManager.setSchedule(bestSchedule); scheduleManager.setSolver(null); } } */ }