Java tutorial
/* * Licensed to The Apereo Foundation under one or more contributor license * agreements. See the NOTICE file distributed with this work for * additional information regarding copyright ownership. * * The Apereo Foundation licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * * See the License for the specific language governing permissions and * limitations under the License. * */ package org.unitime.timetable.solver.exam; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.util.Collection; import java.util.ConcurrentModificationException; import java.util.Date; import java.util.HashSet; import java.util.Hashtable; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeSet; import java.util.Vector; import java.util.concurrent.locks.Lock; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.cpsolver.exam.heuristics.ExamNeighbourSelection; import org.cpsolver.exam.model.Exam; import org.cpsolver.exam.model.ExamInstructor; import org.cpsolver.exam.model.ExamOwner; import org.cpsolver.exam.model.ExamPeriod; import org.cpsolver.exam.model.ExamPeriodPlacement; import org.cpsolver.exam.model.ExamPlacement; import org.cpsolver.exam.model.ExamRoom; import org.cpsolver.exam.model.ExamRoomPlacement; import org.cpsolver.exam.model.ExamRoomSharing; import org.cpsolver.ifs.extension.ConflictStatistics; import org.cpsolver.ifs.extension.Extension; import org.cpsolver.ifs.model.Constraint; import org.cpsolver.ifs.solution.Solution; import org.cpsolver.ifs.solver.ParallelSolver; import org.cpsolver.ifs.util.Callback; import org.cpsolver.ifs.util.DataProperties; import org.cpsolver.ifs.util.Progress; import org.cpsolver.ifs.util.ProgressWriter; import org.cpsolver.ifs.util.ToolBox; import org.dom4j.Document; import org.dom4j.Element; import org.dom4j.io.OutputFormat; import org.dom4j.io.SAXReader; import org.dom4j.io.XMLWriter; import org.unitime.timetable.defaults.ApplicationProperty; import org.unitime.timetable.model.dao.SubjectAreaDAO; import org.unitime.timetable.solver.exam.ui.ExamAssignment; import org.unitime.timetable.solver.exam.ui.ExamAssignmentInfo; import org.unitime.timetable.solver.exam.ui.ExamConflictStatisticsInfo; import org.unitime.timetable.solver.exam.ui.ExamInfo; import org.unitime.timetable.solver.exam.ui.ExamInfoModel; import org.unitime.timetable.solver.exam.ui.ExamProposedChange; import org.unitime.timetable.solver.exam.ui.ExamRoomInfo; import org.unitime.timetable.solver.exam.ui.ExamSuggestionsInfo; import org.unitime.timetable.solver.remote.BackupFileFilter; import org.unitime.timetable.util.Constants; /** * @author Tomas Muller */ public class ExamSolver extends ParallelSolver<Exam, ExamPlacement> implements ExamSolverProxy { private static Log sLog = LogFactory.getLog(ExamSolver.class); private int iDebugLevel = Progress.MSGLEVEL_INFO; private boolean iWorking = false; private Date iLoadedDate = null; private ExamSolverDisposeListener iDisposeListener = null; private ExamConflictStatisticsInfo iCbsInfo = null; private long iLastTimeStamp = System.currentTimeMillis(); private boolean iIsPassivated = false; private Map iProgressBeforePassivation = null; private Map<String, String> iCurrentSolutionInfoBeforePassivation = null; private Map<String, String> iBestSolutionInfoBeforePassivation = null; private File iPassivationFolder = null; private String iPassivationPuid = null; private Thread iWorkThread = null; public ExamSolver(DataProperties properties, ExamSolverDisposeListener disposeListener) { super(properties); iDisposeListener = disposeListener; } public Date getLoadedDate() { if (iLoadedDate == null && !isPassivated()) { List<Progress.Message> log = Progress.getInstance(currentSolution().getModel()).getLog(); if (log != null && !log.isEmpty()) { iLoadedDate = log.get(0).getDate(); } } return iLoadedDate; } public String getLog() { return Progress.getInstance(currentSolution().getModel()).getHtmlLog(iDebugLevel, true); } public String getLog(int level, boolean includeDate) { return Progress.getInstance(currentSolution().getModel()).getHtmlLog(level, includeDate); } public String getLog(int level, boolean includeDate, String fromStage) { return Progress.getInstance(currentSolution().getModel()).getHtmlLog(level, includeDate, fromStage); } public void setDebugLevel(int level) { iDebugLevel = level; } public int getDebugLevel() { return iDebugLevel; } public boolean isWorking() { if (isRunning()) return true; return iWorking; } public void restoreBest() { currentSolution().restoreBest(); } public void saveBest() { currentSolution().saveBest(); } public Map getProgress() { if (isPassivated()) return iProgressBeforePassivation; try { Hashtable ret = new Hashtable(); Progress p = Progress.getInstance(super.currentSolution().getModel()); ret.put("STATUS", p.getStatus()); ret.put("PHASE", p.getPhase()); ret.put("PROGRESS", new Long(p.getProgress())); ret.put("MAX_PROGRESS", new Long(p.getProgressMax())); ret.put("VERSION", Constants.getVersion()); return ret; } catch (Exception e) { sLog.error(e.getMessage(), e); return null; } } public void setProperties(DataProperties properties) { activateIfNeeded(); this.getProperties().putAll(properties); } public void dispose() { disposeNoInherit(true); } private void disposeNoInherit(boolean unregister) { super.dispose(); if (currentSolution() != null && currentSolution().getModel() != null) Progress.removeInstance(currentSolution().getModel()); setInitalSolution((org.cpsolver.ifs.solution.Solution) null); if (unregister && iDisposeListener != null) iDisposeListener.onDispose(); iCbsInfo = null; } public String getHost() { return "local"; } public String getUser() { return getProperties().getProperty("General.OwnerPuid"); } public Object exec(Object[] cmd) throws Exception { Class[] types = new Class[(cmd.length - 3) / 2]; Object[] args = new Object[(cmd.length - 3) / 2]; for (int i = 0; i < types.length; i++) { types[i] = (Class) cmd[2 * i + 3]; args[i] = cmd[2 * i + 4]; } return getClass().getMethod((String) cmd[0], types).invoke(this, args); } public Exam getExam(long examId) { Lock lock = currentSolution().getLock().readLock(); lock.lock(); try { for (Exam exam : currentSolution().getModel().variables()) { if (exam.getId() == examId) return exam; } return null; } finally { lock.unlock(); } } public ExamInfo getInfo(long examId) { Lock lock = currentSolution().getLock().readLock(); lock.lock(); try { Exam exam = getExam(examId); return (exam == null ? null : new ExamInfo(exam)); } finally { lock.unlock(); } } public ExamAssignment getAssignment(long examId) { Lock lock = currentSolution().getLock().readLock(); lock.lock(); try { Exam exam = getExam(examId); ExamPlacement placement = (exam == null ? null : currentSolution().getAssignment().getValue(exam)); return placement == null ? null : new ExamAssignment(placement, currentSolution().getAssignment()); } finally { lock.unlock(); } } public ExamAssignmentInfo getAssignmentInfo(long examId) { Lock lock = currentSolution().getLock().readLock(); lock.lock(); try { Exam exam = getExam(examId); ExamPlacement placement = (exam == null ? null : currentSolution().getAssignment().getValue(exam)); return placement == null ? null : new ExamAssignmentInfo(placement, currentSolution().getAssignment()); } finally { lock.unlock(); } } public ExamPlacement getPlacement(ExamAssignment assignment) { Lock lock = currentSolution().getLock().readLock(); lock.lock(); try { Exam exam = getExam(assignment.getExamId()); if (exam == null) return null; ExamPeriodPlacement period = null; for (ExamPeriodPlacement p : exam.getPeriodPlacements()) { if (p.getId().equals(assignment.getPeriodId())) { period = p; break; } } if (period == null) return null; HashSet rooms = new HashSet(); for (ExamRoomInfo roomInfo : assignment.getRooms()) { Long roomId = roomInfo.getLocationId(); ExamRoomPlacement room = null; for (ExamRoomPlacement r : exam.getRoomPlacements()) { if (r.getId() == roomId) { room = r; break; } } if (room != null) rooms.add(room); } return new ExamPlacement(exam, period, rooms); } finally { lock.unlock(); } } public ExamAssignmentInfo getAssignment(Long examId, Long periodId, Collection<Long> roomIds) { Lock lock = currentSolution().getLock().readLock(); lock.lock(); try { Exam exam = getExam(examId); if (exam == null) return null; ExamPeriodPlacement period = null; for (ExamPeriodPlacement p : exam.getPeriodPlacements()) { if (p.getId().equals(periodId)) { period = p; break; } } if (period == null) return null; HashSet rooms = new HashSet(); for (Long roomId : roomIds) { ExamRoomPlacement room = null; for (ExamRoomPlacement r : exam.getRoomPlacements()) { if (r.getId() == roomId) { room = r; break; } } if (room != null) rooms.add(room); } return new ExamAssignmentInfo(exam, new ExamPlacement(exam, period, rooms), currentSolution().getAssignment()); } finally { lock.unlock(); } } public String assign(ExamAssignment assignment) { Lock lock = currentSolution().getLock().writeLock(); lock.lock(); try { Exam exam = getExam(assignment.getExamId()); if (exam == null) return "Examination " + assignment.getExamName() + " not found."; ExamPeriodPlacement period = null; for (ExamPeriodPlacement p : exam.getPeriodPlacements()) { if (p.getId().equals(assignment.getPeriodId())) { period = p; break; } } if (period == null) return "Examination period " + assignment.getPeriodName() + " is not available for examination " + assignment.getExamName() + "."; HashSet rooms = new HashSet(); for (Iterator i = assignment.getRooms().iterator(); i.hasNext();) { ExamRoomInfo ri = (ExamRoomInfo) i.next(); ExamRoomPlacement room = null; for (ExamRoomPlacement r : exam.getRoomPlacements()) { if (r.getId() == ri.getLocationId()) { room = r; break; } } if (room == null) return "Examination room " + ri.getName() + " not found."; if (!room.isAvailable(period.getPeriod())) return "Examination room " + ri.getName() + " is not available at " + assignment.getPeriodName() + "."; rooms.add(room); } ExamPlacement p = new ExamPlacement(exam, period, rooms); Set conflicts = currentSolution().getModel().conflictValues(currentSolution().getAssignment(), p); if (conflicts.isEmpty()) { ExamPlacement old = currentSolution().getAssignment().getValue(exam); currentSolution().getAssignment().assign(0, p); Progress.getInstance(currentSolution().getModel()).info(exam.getName() + ": " + (old == null ? "not assigned" : old.getName()) + " → " + p.getName()); return null; } else { ExamPlacement other = (ExamPlacement) conflicts.iterator().next(); return "Selected placement " + p.getName() + " is in conflict with exam " + other.variable().getName() + " that is assigned to " + other.getName() + "."; } } finally { lock.unlock(); } } public String unassign(ExamInfo examInfo) { Lock lock = currentSolution().getLock().writeLock(); lock.lock(); try { Exam exam = getExam(examInfo.getExamId()); if (exam == null) return "Examination " + examInfo.getExamName() + " not found."; ExamPlacement placement = currentSolution().getAssignment().getValue(exam); if (placement == null) return "Examination " + examInfo.getExamName() + " is not assigned."; Progress.getInstance(currentSolution().getModel()) .info(exam.getName() + ": " + placement.getName() + " → not assigned"); currentSolution().getAssignment().unassign(0, exam); return null; } finally { lock.unlock(); } } public Map<String, String> currentSolutionInfo() { if (isPassivated()) return iCurrentSolutionInfoBeforePassivation; Lock lock = currentSolution().getLock().readLock(); lock.lock(); try { return super.currentSolution().getInfo(); } finally { lock.unlock(); } } public Map<String, String> bestSolutionInfo() { if (isPassivated()) return iBestSolutionInfoBeforePassivation; Lock lock = currentSolution().getLock().readLock(); lock.lock(); try { return super.currentSolution().getBestInfo(); } finally { lock.unlock(); } } protected void onFinish() { super.onFinish(); try { iWorking = true; if (currentSolution().getBestInfo() != null) currentSolution().restoreBest(); if (currentSolution().getBestInfo() != null && getProperties().getPropertyBoolean("General.Save", false)) { ExamDatabaseSaver saver = new ExamDatabaseSaver(this); Lock lock = currentSolution().getLock().readLock(); lock.lock(); try { saver.save(); } finally { lock.unlock(); } } if (getProperties().getPropertyBoolean("General.Unload", false)) { dispose(); } else { Progress.getInstance(currentSolution().getModel()).setStatus("Awaiting commands ..."); } } finally { iWorking = false; } } protected void onStop() { super.onStop(); if (currentSolution().getBestInfo() != null) currentSolution().restoreBest(); } public void save() { iWorking = true; ExamDatabaseSaver saver = new ExamDatabaseSaver(this); saver.setCallback(getSavingDoneCallback()); iWorkThread = new Thread(saver); iWorkThread.setPriority(THREAD_PRIORITY); iWorkThread.start(); } public void load(DataProperties properties) { iCbsInfo = null; setProperties(properties); ExamModel model = new ExamModel(getProperties()); Progress.getInstance(model).addProgressListener(new ProgressWriter(System.out)); iWorking = true; setInitalSolution(model); initSolver(); ExamDatabaseLoader loader = new ExamDatabaseLoader(model, currentSolution().getAssignment()); loader.setCallback(getLoadingDoneCallback()); iWorkThread = new Thread(loader); iWorkThread.setPriority(THREAD_PRIORITY); iWorkThread.start(); } public void reload(DataProperties properties) { if (currentSolution() == null || currentSolution().getModel() == null) { load(properties); return; } Callback callBack = getReloadingDoneCallback(); setProperties(properties); ExamModel model = new ExamModel(getProperties()); iWorking = true; Progress.changeInstance(currentSolution().getModel(), model); setInitalSolution(model); initSolver(); ExamDatabaseLoader loader = new ExamDatabaseLoader(model, currentSolution().getAssignment()); loader.setCallback(callBack); iWorkThread = new Thread(loader); iWorkThread.start(); } public Callback getLoadingDoneCallback() { return new LoadingDoneCallback(); } public Callback getReloadingDoneCallback() { return new ReloadingDoneCallback(); } public Callback getSavingDoneCallback() { return new SavingDoneCallback(); } protected void afterSave() { } protected void afterLoad() { } protected void afterFinalSectioning() { } public class ReloadingDoneCallback implements Callback { Hashtable iCurrentAssignmentTable = new Hashtable(); Hashtable iBestAssignmentTable = new Hashtable(); Hashtable iInitialAssignmentTable = new Hashtable(); String iSolutionId = null; Progress iProgress = null; public ReloadingDoneCallback() { iSolutionId = getProperties().getProperty("General.SolutionId"); for (Exam exam : currentSolution().getModel().variables()) { ExamPlacement placement = currentSolution().getAssignment().getValue(exam); if (placement != null) iCurrentAssignmentTable.put(exam.getId(), placement); if (exam.getBestAssignment() != null) iBestAssignmentTable.put(exam.getId(), exam.getBestAssignment()); if (exam.getInitialAssignment() != null) iInitialAssignmentTable.put(exam.getId(), exam.getInitialAssignment()); } } private Exam getExam(long examId) { for (Exam exam : currentSolution().getModel().variables()) { if (exam.getId() == examId) return exam; } return null; } private ExamPlacement getPlacement(Exam exam, ExamPlacement placement) { ExamPeriodPlacement period = null; for (ExamPeriodPlacement p : exam.getPeriodPlacements()) { if (placement.getPeriod().equals(p.getPeriod())) { period = p; break; } } if (period == null) { iProgress.warn("WARNING: Period " + placement.getPeriod() + " is not available for class " + exam.getName()); return null; } Set rooms = new HashSet(); for (Iterator f = exam.getRoomPlacements().iterator(); f.hasNext();) { ExamRoomPlacement r = (ExamRoomPlacement) f.next(); if (r.isAvailable(period.getPeriod()) && placement.contains(r.getRoom())) { rooms.add(r); } } if (rooms.size() != placement.getRoomPlacements().size()) { iProgress.warn("WARNING: Room(s) " + placement.getRoomPlacements() + " are not available for exam " + exam.getName()); return null; } return new ExamPlacement(exam, period, rooms); } private void assign(ExamPlacement placement) { Map<Constraint<Exam, ExamPlacement>, Set<ExamPlacement>> conflictConstraints = currentSolution() .getModel().conflictConstraints(currentSolution().getAssignment(), placement); if (conflictConstraints.isEmpty()) { currentSolution().getAssignment().assign(0, placement); } else { iProgress.warn("Unable to assign " + placement.variable().getName() + " := " + placement.getName()); iProgress.warn(" Reason:"); for (Constraint<Exam, ExamPlacement> c : conflictConstraints.keySet()) { Set<ExamPlacement> vals = conflictConstraints.get(c); for (ExamPlacement v : vals) { iProgress.warn(" " + v.variable().getName() + " = " + v.getName()); } iProgress.debug(" in constraint " + c); } } } private void unassignAll() { for (Exam exam : currentSolution().getModel().variables()) { currentSolution().getAssignment().unassign(0, exam); } } public void execute() { iProgress = Progress.getInstance(currentSolution().getModel()); if (!iBestAssignmentTable.isEmpty()) { iProgress.setPhase("Creating best assignment ...", iBestAssignmentTable.size()); unassignAll(); for (Iterator i = iBestAssignmentTable.entrySet().iterator(); i.hasNext();) { Map.Entry entry = (Map.Entry) i.next(); iProgress.incProgress(); Exam exam = getExam((Long) entry.getKey()); if (exam == null) continue; ExamPlacement placement = getPlacement(exam, (ExamPlacement) entry.getValue()); if (placement != null) assign(placement); } currentSolution().saveBest(); } if (!iInitialAssignmentTable.isEmpty()) { iProgress.setPhase("Creating initial assignment ...", iInitialAssignmentTable.size()); for (Iterator i = iInitialAssignmentTable.entrySet().iterator(); i.hasNext();) { Map.Entry entry = (Map.Entry) i.next(); iProgress.incProgress(); Exam exam = getExam((Long) entry.getKey()); if (exam == null) continue; ExamPlacement placement = getPlacement(exam, (ExamPlacement) entry.getValue()); if (placement != null) exam.setInitialAssignment(placement); } } if (!iCurrentAssignmentTable.isEmpty()) { iProgress.setPhase("Creating current assignment ...", iCurrentAssignmentTable.size()); unassignAll(); for (Iterator i = iCurrentAssignmentTable.entrySet().iterator(); i.hasNext();) { Map.Entry entry = (Map.Entry) i.next(); iProgress.incProgress(); Exam exam = getExam((Long) entry.getKey()); if (exam == null) continue; ExamPlacement placement = getPlacement(exam, (ExamPlacement) entry.getValue()); if (placement != null) assign(placement); } } iCurrentAssignmentTable.clear(); iBestAssignmentTable.clear(); iInitialAssignmentTable.clear(); iProgress = null; if (iSolutionId != null) getProperties().setProperty("General.SolutionId", iSolutionId); iLoadedDate = new Date(); iWorking = false; afterLoad(); Progress.getInstance(currentSolution().getModel()).setStatus("Awaiting commands ..."); } } public class LoadingDoneCallback implements Callback { public void execute() { iLoadedDate = new Date(); iWorking = false; afterLoad(); Progress.getInstance(currentSolution().getModel()).setStatus("Awaiting commands ..."); if (getProperties().getPropertyBoolean("General.StartSolver", false)) start(); } } public class SavingDoneCallback implements Callback { public void execute() { iWorking = false; afterSave(); Progress.getInstance(currentSolution().getModel()).setStatus("Awaiting commands ..."); } } public static interface ExamSolverDisposeListener { public void onDispose(); } public boolean backup(File folder, String puid) { folder.mkdirs(); if (currentSolution() == null) return false; Lock lock = currentSolution().getLock().readLock(); lock.lock(); try { File outXmlFile = new File(folder, "exam_" + puid + BackupFileFilter.sXmlExtension); File outPropertiesFile = new File(folder, "exam_" + puid + BackupFileFilter.sPropertiesExtension); try { getProperties().setProperty("Xml.SaveConflictTable", "false"); FileOutputStream fos = null; try { fos = new FileOutputStream(outXmlFile); Document document = ((ExamModel) currentSolution().getModel()) .save(currentSolution().getAssignment()); ExamConflictStatisticsInfo cbsInfo = getCbsInfo(); if (cbsInfo != null) cbsInfo.save(document.getRootElement().addElement("cbsInfo")); (new XMLWriter(fos, OutputFormat.createPrettyPrint())).write(document); fos.flush(); fos.close(); fos = null; } finally { try { if (fos != null) fos.close(); } catch (IOException e) { } } for (Iterator i = getProperties().entrySet().iterator(); i.hasNext();) { Map.Entry entry = (Map.Entry) i.next(); if (!(entry.getKey() instanceof String)) { sLog.error("Configuration key " + entry.getKey() + " is not of type String (" + entry.getKey().getClass() + ")"); i.remove(); } else if (!(entry.getValue() instanceof String)) { sLog.error("Value of configuration key " + entry.getKey() + " is not of type String (" + entry.getValue() + " is of type " + entry.getValue().getClass() + ")"); i.remove(); } } try { fos = new FileOutputStream(outPropertiesFile); getProperties().store(fos, "Backup file"); fos.flush(); fos.close(); fos = null; } finally { try { if (fos != null) fos.close(); } catch (IOException e) { } } return true; } catch (Exception e) { sLog.error(e.getMessage(), e); if (outXmlFile.exists()) outXmlFile.delete(); if (outPropertiesFile.exists()) outPropertiesFile.delete(); } } finally { lock.unlock(); } return false; } public boolean restore(File folder, String puid) { return restore(folder, puid, false); } public boolean restore(File folder, String puid, boolean removeFiles) { sLog.debug("restore(folder=" + folder + "," + puid + ",exam)"); iCbsInfo = null; File inXmlFile = new File(folder, "exam_" + puid + BackupFileFilter.sXmlExtension); File inPropertiesFile = new File(folder, "exam_" + puid + BackupFileFilter.sPropertiesExtension); ExamModel model = null; try { if (isRunning()) stopSolver(); disposeNoInherit(false); FileInputStream fis = null; try { fis = new FileInputStream(inPropertiesFile); getProperties().load(fis); } finally { if (fis != null) fis.close(); } model = new ExamModel(getProperties()); Progress.getInstance(model).addProgressListener(new ProgressWriter(System.out)); setInitalSolution(model); initSolver(); Document document = (new SAXReader()).read(inXmlFile); model.load(document, currentSolution().getAssignment(), new Callback() { public void execute() { saveBest(); } }); if (document.getRootElement().element("cbsInfo") != null) { iCbsInfo = new ExamConflictStatisticsInfo(); iCbsInfo.load(document.getRootElement().element("cbsInfo")); } Progress.getInstance(model).setStatus("Awaiting commands ..."); if (removeFiles) { inXmlFile.delete(); inPropertiesFile.delete(); } return true; } catch (Exception e) { sLog.error(e.getMessage(), e); if (model != null) Progress.removeInstance(model); } return false; } public void clear() { Lock lock = currentSolution().getLock().writeLock(); lock.lock(); try { for (Exam exam : currentSolution().getModel().variables()) { currentSolution().getAssignment().unassign(0, exam); } currentSolution().clearBest(); } finally { lock.unlock(); } } public Collection<ExamAssignmentInfo> getAssignedExams() { Lock lock = currentSolution().getLock().readLock(); lock.lock(); try { Vector<ExamAssignmentInfo> ret = new Vector<ExamAssignmentInfo>(); for (Exam exam : currentSolution().getModel().variables()) { ExamPlacement placement = currentSolution().getAssignment().getValue(exam); if (placement != null) ret.add(new ExamAssignmentInfo(placement, currentSolution().getAssignment())); } return ret; } finally { lock.unlock(); } } public Collection<ExamInfo> getUnassignedExams() { Lock lock = currentSolution().getLock().readLock(); lock.lock(); try { Vector<ExamInfo> ret = new Vector<ExamInfo>(); for (Exam exam : currentSolution().getModel().variables()) { ExamPlacement placement = currentSolution().getAssignment().getValue(exam); if (placement == null) ret.add(new ExamInfo(exam)); } return ret; } finally { lock.unlock(); } } public Collection<ExamAssignmentInfo> getAssignedExams(Long subjectAreaId) { if (subjectAreaId == null || subjectAreaId < 0) return getAssignedExams(); String sa = new SubjectAreaDAO().get(subjectAreaId).getSubjectAreaAbbreviation() + " "; Lock lock = currentSolution().getLock().readLock(); lock.lock(); try { Vector<ExamAssignmentInfo> ret = new Vector<ExamAssignmentInfo>(); for (Exam exam : currentSolution().getModel().variables()) { boolean hasSubjectArea = false; for (Iterator<ExamOwner> f = exam.getOwners().iterator(); !hasSubjectArea && f.hasNext();) { ExamOwner ecs = (ExamOwner) f.next(); hasSubjectArea = ecs.getName().startsWith(sa); } if (hasSubjectArea) { ExamPlacement placement = currentSolution().getAssignment().getValue(exam); if (placement != null) ret.add(new ExamAssignmentInfo(placement, currentSolution().getAssignment())); } } return ret; } finally { lock.unlock(); } } public Collection<ExamInfo> getUnassignedExams(Long subjectAreaId) { if (subjectAreaId == null || subjectAreaId < 0) return getUnassignedExams(); String sa = new SubjectAreaDAO().get(subjectAreaId).getSubjectAreaAbbreviation() + " "; Lock lock = currentSolution().getLock().readLock(); lock.lock(); try { Vector<ExamInfo> ret = new Vector<ExamInfo>(); for (Exam exam : currentSolution().getModel().variables()) { boolean hasSubjectArea = false; for (Iterator<ExamOwner> f = exam.getOwners().iterator(); !hasSubjectArea && f.hasNext();) { ExamOwner ecs = f.next(); hasSubjectArea = ecs.getName().startsWith(sa); } if (hasSubjectArea) { ExamPlacement placement = currentSolution().getAssignment().getValue(exam); if (placement == null) ret.add(new ExamInfo(exam)); } } return ret; } finally { lock.unlock(); } } public Collection<ExamAssignmentInfo> getAssignedExamsOfRoom(Long roomId) { Lock lock = currentSolution().getLock().readLock(); lock.lock(); try { ExamRoom room = null; for (ExamRoom r : ((ExamModel) currentSolution().getModel()).getRooms()) { if (r.getId() == roomId) { room = r; break; } } if (room == null) return null; Vector<ExamAssignmentInfo> ret = new Vector<ExamAssignmentInfo>(); for (ExamPeriod period : ((ExamModel) currentSolution().getModel()).getPeriods()) { for (ExamPlacement placement : room.getPlacements(currentSolution().getAssignment(), period)) { ret.add(new ExamAssignmentInfo(placement, currentSolution().getAssignment())); } } return ret; } finally { lock.unlock(); } } public Collection<ExamAssignmentInfo> getAssignedExamsOfInstructor(Long instructorId) { Lock lock = currentSolution().getLock().readLock(); lock.lock(); try { ExamInstructor instructor = null; for (ExamInstructor i : ((ExamModel) currentSolution().getModel()).getInstructors()) { if (i.getId() == instructorId) { instructor = i; break; } } if (instructor == null) return null; Vector<ExamAssignmentInfo> ret = new Vector<ExamAssignmentInfo>(); for (ExamPeriod period : ((ExamModel) currentSolution().getModel()).getPeriods()) { Set exams = instructor.getExams(currentSolution().getAssignment(), period); if (exams != null) for (Iterator i = exams.iterator(); i.hasNext();) { Exam exam = (Exam) i.next(); ret.add(new ExamAssignmentInfo(currentSolution().getAssignment().getValue(exam), currentSolution().getAssignment())); } } return ret; } finally { lock.unlock(); } } public Long getExamTypeId() { return getProperties().getPropertyLong("Exam.Type", null); } public Collection<ExamAssignmentInfo[]> getChangesToInitial(Long subjectAreaId) { String sa = (subjectAreaId != null && subjectAreaId >= 0 ? new SubjectAreaDAO().get(subjectAreaId).getSubjectAreaAbbreviation() + " " : null); Vector<ExamAssignmentInfo[]> changes = new Vector<ExamAssignmentInfo[]>(); Lock lock = currentSolution().getLock().readLock(); lock.lock(); try { for (Exam exam : currentSolution().getModel().variables()) { if (sa != null) { boolean hasSubjectArea = false; for (Iterator<ExamOwner> f = exam.getOwners().iterator(); !hasSubjectArea && f.hasNext();) { ExamOwner ecs = f.next(); hasSubjectArea = ecs.getName().startsWith(sa); } if (!hasSubjectArea) continue; } if (!ToolBox.equals(exam.getInitialAssignment(), currentSolution().getAssignment().getValue(exam))) { changes.add(new ExamAssignmentInfo[] { new ExamAssignmentInfo(exam, exam.getInitialAssignment(), currentSolution().getAssignment()), new ExamAssignmentInfo(exam, currentSolution().getAssignment().getValue(exam), currentSolution().getAssignment()) }); } } } finally { lock.unlock(); } return changes; } public Collection<ExamAssignmentInfo[]> getChangesToBest(Long subjectAreaId) { String sa = (subjectAreaId != null && subjectAreaId >= 0 ? new SubjectAreaDAO().get(subjectAreaId).getSubjectAreaAbbreviation() + " " : null); Vector<ExamAssignmentInfo[]> changes = new Vector<ExamAssignmentInfo[]>(); Lock lock = currentSolution().getLock().readLock(); lock.lock(); try { for (Exam exam : currentSolution().getModel().variables()) { if (sa != null) { boolean hasSubjectArea = false; for (Iterator<ExamOwner> f = exam.getOwners().iterator(); !hasSubjectArea && f.hasNext();) { ExamOwner ecs = f.next(); hasSubjectArea = ecs.getName().startsWith(sa); } if (!hasSubjectArea) continue; } if (!ToolBox.equals(exam.getBestAssignment(), currentSolution().getAssignment().getValue(exam))) { changes.add(new ExamAssignmentInfo[] { new ExamAssignmentInfo(exam, exam.getBestAssignment(), currentSolution().getAssignment()), new ExamAssignmentInfo(exam, currentSolution().getAssignment().getValue(exam), currentSolution().getAssignment()) }); } } } finally { lock.unlock(); } return changes; } public Long getSessionId() { return getProperties().getPropertyLong("General.SessionId", null); } public ExamConflictStatisticsInfo getCbsInfo() { ConflictStatistics cbs = null; for (Extension ext : getExtensions()) { if (ext instanceof ConflictStatistics) { cbs = (ConflictStatistics) ext; break; } } if (cbs == null || cbs.getNoGoods().isEmpty()) { if (iCbsInfo != null) return iCbsInfo; return null; } ExamConflictStatisticsInfo info = new ExamConflictStatisticsInfo(); Lock lock = currentSolution().getLock().readLock(); lock.lock(); try { info.load(cbs); } finally { lock.unlock(); } return info; } public ExamConflictStatisticsInfo getCbsInfo(Long examId) { ConflictStatistics cbs = null; for (Extension ext : getExtensions()) { if (ext instanceof ConflictStatistics) { cbs = (ConflictStatistics) ext; break; } } if (cbs == null || cbs.getNoGoods().isEmpty()) { if (iCbsInfo != null) return iCbsInfo; return null; } ExamConflictStatisticsInfo info = new ExamConflictStatisticsInfo(); Lock lock = currentSolution().getLock().readLock(); lock.lock(); try { info.load(cbs, examId); } finally { lock.unlock(); } return info; } public ExamProposedChange update(ExamProposedChange change) { Lock lock = currentSolution().getLock().writeLock(); lock.lock(); try { Hashtable<Exam, ExamPlacement> undoAssign = new Hashtable(); HashSet<Exam> undoUnassing = new HashSet(); Vector<Exam> unassign = new Vector(); Vector<ExamPlacement> assign = new Vector(); HashSet<ExamPlacement> conflicts = new HashSet(); /* for (ExamAssignment assignment: change.getConflicts()) { ExamPlacement placement = getPlacement(assignment); if (placement==null || !placement.equals(placement.variable().getAssignment())) return null; undoAssign.put((Exam)placement.variable(),placement); unassign.add((Exam)placement.variable()); } */ for (ExamAssignment assignment : change.getAssignments()) { ExamPlacement placement = getPlacement(assignment); if (placement == null) return null; if (currentSolution().getAssignment().getValue(placement.variable()) != null) undoAssign.put((Exam) placement.variable(), currentSolution().getAssignment().getValue(placement.variable())); else undoUnassing.add((Exam) placement.variable()); assign.add(placement); unassign.add((Exam) placement.variable()); } for (Exam exam : unassign) currentSolution().getAssignment().unassign(0, exam); for (ExamPlacement placement : assign) { for (Iterator i = placement.variable().getModel() .conflictValues(currentSolution().getAssignment(), placement).iterator(); i.hasNext();) { ExamPlacement conflict = (ExamPlacement) i.next(); Exam conflictingExam = (Exam) conflict.variable(); if (!undoAssign.containsKey(conflictingExam) && !undoUnassing.contains(conflictingExam)) undoAssign.put(conflictingExam, conflict); conflicts.add(conflict); currentSolution().getAssignment().unassign(0, conflict.variable()); } currentSolution().getAssignment().assign(0, placement); } change.getAssignments().clear(); change.getConflicts().clear(); for (ExamPlacement assignment : assign) if (!conflicts.contains(assignment)) change.getAssignments() .add(new ExamAssignmentInfo(assignment, currentSolution().getAssignment())); for (Exam exam : undoUnassing) if (currentSolution().getAssignment().getValue(exam) != null) currentSolution().getAssignment().unassign(0, exam); for (Map.Entry<Exam, ExamPlacement> entry : undoAssign.entrySet()) currentSolution().getAssignment().unassign(0, entry.getKey()); for (Map.Entry<Exam, ExamPlacement> entry : undoAssign.entrySet()) currentSolution().getAssignment().assign(0, entry.getValue()); for (ExamPlacement conflict : conflicts) { ExamPlacement original = undoAssign.get((Exam) conflict.variable()); change.getConflicts().add(new ExamAssignment(original == null ? conflict : original, currentSolution().getAssignment())); } return change; } finally { lock.unlock(); } } public Vector<ExamRoomInfo> getRooms(long examId, long periodId, ExamProposedChange change, int minRoomSize, int maxRoomSize, String filter, boolean allowConflicts) { Lock lock = currentSolution().getLock().readLock(); lock.lock(); try { //lookup exam, period etc. Exam exam = getExam(examId); if (exam == null) return null; ExamPeriodPlacement period = null; for (ExamPeriodPlacement p : exam.getPeriodPlacements()) { if (periodId == p.getId()) { period = p; break; } } if (period == null) return null; Vector<ExamRoomInfo> rooms = new Vector<ExamRoomInfo>(); if (exam.getMaxRooms() == 0) return rooms; //assign change Hashtable<Exam, ExamPlacement> undoAssign = new Hashtable(); HashSet<Exam> undoUnassing = new HashSet(); if (change != null) { for (ExamAssignment assignment : change.getConflicts()) { ExamPlacement placement = getPlacement(assignment); if (placement == null) continue; undoAssign.put((Exam) placement.variable(), placement); currentSolution().getAssignment().unassign(0, placement.variable()); } for (ExamAssignment assignment : change.getAssignments()) { ExamPlacement placement = getPlacement(assignment); if (placement == null) continue; for (Iterator i = placement.variable().getModel() .conflictValues(currentSolution().getAssignment(), placement).iterator(); i .hasNext();) { ExamPlacement conflict = (ExamPlacement) i.next(); if (conflict.variable().equals(placement.variable())) continue; Exam conflictingExam = (Exam) conflict.variable(); if (!undoAssign.containsKey(conflictingExam) && !undoUnassing.contains(conflictingExam)) undoAssign.put(conflictingExam, conflict); currentSolution().getAssignment().unassign(0, conflict.variable()); } if (currentSolution().getAssignment().getValue(placement.variable()) != null) undoAssign.put((Exam) placement.variable(), currentSolution().getAssignment().getValue(placement.variable())); else undoUnassing.add((Exam) placement.variable()); currentSolution().getAssignment().assign(0, placement); } } ExamRoomSharing sharing = ((ExamModel) currentSolution().getModel()).getRoomSharing(); //compute rooms for (ExamRoomPlacement room : exam.getRoomPlacements()) { int cap = room.getSize(exam.hasAltSeating()); if (minRoomSize >= 0 && cap < minRoomSize) continue; if (maxRoomSize >= 0 && cap > maxRoomSize) continue; if (!ExamInfoModel.match(room.getName(), filter)) continue; if (!room.isAvailable(period.getPeriod())) continue; boolean conf = !exam.checkDistributionConstraints(currentSolution().getAssignment(), room); if (sharing == null) { for (ExamPlacement p : room.getRoom().getPlacements(currentSolution().getAssignment(), period.getPeriod())) if (!p.variable().equals(exam)) conf = true; } else { if (sharing.inConflict(exam, room.getRoom().getPlacements(currentSolution().getAssignment(), period.getPeriod()), room.getRoom())) conf = true; } if (!allowConflicts && conf) continue; rooms.add(new ExamRoomInfo(room.getRoom(), (conf ? 100 : 0) + room.getPenalty(period.getPeriod()))); } //undo change for (Exam undoExam : undoUnassing) if (currentSolution().getAssignment().getValue(undoExam) != null) currentSolution().getAssignment().unassign(0, undoExam); for (Map.Entry<Exam, ExamPlacement> entry : undoAssign.entrySet()) currentSolution().getAssignment().unassign(0, entry.getKey()); for (Map.Entry<Exam, ExamPlacement> entry : undoAssign.entrySet()) currentSolution().getAssignment().assign(0, entry.getValue()); return rooms; } finally { lock.unlock(); } } public Collection<ExamAssignmentInfo> getPeriods(long examId, ExamProposedChange change) { Lock lock = currentSolution().getLock().readLock(); lock.lock(); try { //lookup exam Exam exam = getExam(examId); if (exam == null) return null; //assign change Hashtable<Exam, ExamPlacement> undoAssign = new Hashtable(); HashSet<Exam> undoUnassing = new HashSet(); if (change != null) { for (ExamAssignment assignment : change.getConflicts()) { ExamPlacement placement = getPlacement(assignment); if (placement == null) continue; undoAssign.put((Exam) placement.variable(), placement); currentSolution().getAssignment().unassign(0, placement.variable()); } for (ExamAssignment assignment : change.getAssignments()) { ExamPlacement placement = getPlacement(assignment); if (placement == null) continue; for (Iterator i = placement.variable().getModel() .conflictValues(currentSolution().getAssignment(), placement).iterator(); i .hasNext();) { ExamPlacement conflict = (ExamPlacement) i.next(); if (conflict.variable().equals(placement.variable())) continue; Exam conflictingExam = (Exam) conflict.variable(); if (!undoAssign.containsKey(conflictingExam) && !undoUnassing.contains(conflictingExam)) undoAssign.put(conflictingExam, conflict); currentSolution().getAssignment().unassign(0, conflict.variable()); } if (currentSolution().getAssignment().getValue(placement.variable()) != null) undoAssign.put((Exam) placement.variable(), currentSolution().getAssignment().getValue(placement.variable())); else undoUnassing.add((Exam) placement.variable()); currentSolution().getAssignment().assign(0, placement); } } Vector<ExamAssignmentInfo> periods = new Vector<ExamAssignmentInfo>(); for (ExamPeriodPlacement period : exam.getPeriodPlacements()) { Set rooms = exam.findBestAvailableRooms(currentSolution().getAssignment(), period); if (rooms == null) rooms = new HashSet(); boolean conf = !exam.checkDistributionConstraints(currentSolution().getAssignment(), period); ExamAssignmentInfo assignment = new ExamAssignmentInfo(new ExamPlacement(exam, period, rooms), currentSolution().getAssignment()); if (conf) assignment.setPeriodPref("P"); periods.add(assignment); } //undo change for (Exam undoExam : undoUnassing) if (currentSolution().getAssignment().getValue(undoExam) != null) currentSolution().getAssignment().unassign(0, undoExam); for (Map.Entry<Exam, ExamPlacement> entry : undoAssign.entrySet()) currentSolution().getAssignment().unassign(0, entry.getKey()); for (Map.Entry<Exam, ExamPlacement> entry : undoAssign.entrySet()) currentSolution().getAssignment().assign(0, entry.getValue()); return periods; } finally { lock.unlock(); } } public ExamSuggestionsInfo getSuggestions(long examId, ExamProposedChange change, String filter, int depth, int limit, long timeOut) { Lock lock = currentSolution().getLock().writeLock(); lock.lock(); try { Exam exam = getExam(examId); if (exam == null) return null; ExamSuggestions s = new ExamSuggestions(this); s.setDepth(depth); s.setFilter(filter); s.setLimit(limit); s.setTimeOut(timeOut); TreeSet<ExamProposedChange> suggestions = s.computeSuggestions(exam, (change == null ? null : change.getAssignments())); String message = null; if (s.wasTimeoutReached()) { message = "(" + (timeOut / 1000l) + "s timeout reached, " + s.getNrCombinationsConsidered() + " possibilities up to " + depth + " changes were considered, "; } else { message = "(all " + s.getNrCombinationsConsidered() + " possibilities up to " + depth + " changes were considered, "; } if (suggestions.isEmpty()) { message += "no suggestion found)"; } else if (s.getNrSolutions() > suggestions.size()) { message += "top " + suggestions.size() + " of " + s.getNrSolutions() + " suggestions displayed)"; } else { message += suggestions.size() + " suggestions displayed)"; } return new ExamSuggestionsInfo(suggestions, message, s.wasTimeoutReached()); } finally { lock.unlock(); } } protected void autoConfigure() { super.autoConfigure(); setPerturbationsCounter(null); } public TreeSet<ExamAssignment> getExamsOfRoom(long locationId) { Lock lock = currentSolution().getLock().readLock(); lock.lock(); try { ExamModel model = (ExamModel) currentSolution().getModel(); ExamRoom room = null; for (ExamRoom r : model.getRooms()) { if (r.getId() == locationId) { room = r; break; } } if (room == null) return null; TreeSet<ExamAssignment> ret = new TreeSet(); for (ExamPeriod period : model.getPeriods()) { for (ExamPlacement placement : room.getPlacements(currentSolution().getAssignment(), period)) ret.add(new ExamAssignment(placement, currentSolution().getAssignment())); } return ret; } finally { lock.unlock(); } } public void stopSolverImmediately() { super.stopSolver(); } public void stopSolver() { if (getNeighbourSelection() instanceof ExamNeighbourSelection) { ExamNeighbourSelection xn = (ExamNeighbourSelection) getNeighbourSelection(); if (xn.isFinalPhase()) stopSolverImmediately(); else xn.setFinalPhase(new Callback() { public void execute() { iStop = true; } }); } else stopSolverImmediately(); } public Solution<Exam, ExamPlacement> currentSolution() { activateIfNeeded(); return super.currentSolution(); } public void start() { activateIfNeeded(); iCbsInfo = null; super.start(); } public synchronized boolean isPassivated() { return iIsPassivated; } public synchronized long timeFromLastUsed() { return System.currentTimeMillis() - iLastTimeStamp; } public synchronized boolean activateIfNeeded() { iLastTimeStamp = System.currentTimeMillis(); if (!isPassivated()) return false; sLog.debug("<activate " + iPassivationPuid + ">"); iIsPassivated = false; System.gc(); sLog.debug(" -- memory usage before activation:" + org.unitime.commons.Debug.getMem()); restore(iPassivationFolder, iPassivationPuid, true); System.gc(); sLog.debug(" -- memory usage after activation:" + org.unitime.commons.Debug.getMem()); return true; } public synchronized boolean passivate(File folder, String puid) { if (isPassivated() || super.currentSolution() == null || super.currentSolution().getModel() == null) return false; sLog.debug("<passivate " + puid + ">"); System.gc(); sLog.debug(" -- memory usage before passivation:" + org.unitime.commons.Debug.getMem()); iProgressBeforePassivation = getProgress(); if (iProgressBeforePassivation != null) iProgressBeforePassivation.put("STATUS", "Pasivated"); iCurrentSolutionInfoBeforePassivation = currentSolutionInfo(); iBestSolutionInfoBeforePassivation = bestSolutionInfo(); iPassivationFolder = folder; iPassivationPuid = puid; backup(iPassivationFolder, iPassivationPuid); disposeNoInherit(false); System.gc(); sLog.debug(" -- memory usage after passivation:" + org.unitime.commons.Debug.getMem()); iIsPassivated = true; return true; } public synchronized boolean passivateIfNeeded(File folder, String puid) { long inactiveTimeToPassivate = 60000l * ApplicationProperty.SolverPasivationTime.intValue(); if (isPassivated() || inactiveTimeToPassivate <= 0 || timeFromLastUsed() < inactiveTimeToPassivate || isWorking()) return false; return passivate(folder, puid); } public Date getLastUsed() { return new Date(iLastTimeStamp); } public void interrupt() { try { if (iSolverThread != null) { iStop = true; if (iSolverThread.isAlive() && !iSolverThread.isInterrupted()) iSolverThread.interrupt(); } if (iWorkThread != null && iWorkThread.isAlive() && !iWorkThread.isInterrupted()) { iWorkThread.interrupt(); } } catch (Exception e) { sLog.error("Unable to interrupt the solver, reason: " + e.getMessage(), e); } } public Map<String, String> statusSolutionInfo() { if (isPassivated()) return (iBestSolutionInfoBeforePassivation == null ? iCurrentSolutionInfoBeforePassivation : iBestSolutionInfoBeforePassivation); Lock lock = currentSolution().getLock().readLock(); lock.lock(); try { Map<String, String> info = super.currentSolution().getBestInfo(); try { Solution<Exam, ExamPlacement> solution = getWorkingSolution(); if (info == null || getSolutionComparator().isBetterThanBestSolution(solution)) info = solution.getModel().getInfo(solution.getAssignment()); } catch (ConcurrentModificationException e) { } return info; } finally { lock.unlock(); } } public byte[] exportXml() throws Exception { Lock lock = currentSolution().getLock().readLock(); lock.lock(); try { boolean anonymize = ApplicationProperty.SolverXMLExportNames.isFalse(); boolean idconv = ApplicationProperty.SolverXMLExportConvertIds.isTrue(); if (anonymize) { getProperties().setProperty("Xml.Anonymize", "true"); getProperties().setProperty("Xml.ShowNames", "false"); getProperties().setProperty("Xml.ConvertIds", idconv ? "true" : "false"); getProperties().setProperty("Xml.Anonymize", "true"); getProperties().setProperty("Xml.SaveInitial", "false"); getProperties().setProperty("Xml.SaveBest", "false"); getProperties().setProperty("Xml.SaveSolution", "true"); } ByteArrayOutputStream ret = new ByteArrayOutputStream(); Document document = ((ExamModel) currentSolution().getModel()).save(currentSolution().getAssignment()); if (anonymize) { Element log = document.getRootElement().element("log"); if (log != null) document.getRootElement().remove(log); Element notavailable = document.getRootElement().element("notavailable"); if (notavailable != null) document.getRootElement().remove(notavailable); } (new XMLWriter(ret, OutputFormat.createPrettyPrint())).write(document); ret.flush(); ret.close(); if (anonymize) { getProperties().setProperty("Xml.Anonymize", "false"); getProperties().setProperty("Xml.ConvertIds", "false"); getProperties().setProperty("Xml.ShowNames", "true"); getProperties().remove("Xml.SaveInitial"); getProperties().remove("Xml.SaveBest"); getProperties().remove("Xml.SaveSolution"); } return ret.toByteArray(); } finally { lock.unlock(); } } }