001package org.cpsolver.exam; 002 003import java.io.File; 004import java.io.FileInputStream; 005import java.io.FileOutputStream; 006import java.io.FileWriter; 007import java.io.IOException; 008import java.io.PrintWriter; 009import java.text.DecimalFormat; 010import java.util.Collection; 011import java.util.Locale; 012import java.util.Map; 013 014 015import org.apache.log4j.ConsoleAppender; 016import org.apache.log4j.FileAppender; 017import org.apache.log4j.Level; 018import org.apache.log4j.Logger; 019import org.apache.log4j.PatternLayout; 020import org.cpsolver.exam.criteria.DistributionPenalty; 021import org.cpsolver.exam.criteria.ExamRotationPenalty; 022import org.cpsolver.exam.criteria.InstructorBackToBackConflicts; 023import org.cpsolver.exam.criteria.InstructorDirectConflicts; 024import org.cpsolver.exam.criteria.InstructorDistanceBackToBackConflicts; 025import org.cpsolver.exam.criteria.InstructorMoreThan2ADayConflicts; 026import org.cpsolver.exam.criteria.InstructorNotAvailableConflicts; 027import org.cpsolver.exam.criteria.LargeExamsPenalty; 028import org.cpsolver.exam.criteria.PeriodIndexPenalty; 029import org.cpsolver.exam.criteria.PeriodPenalty; 030import org.cpsolver.exam.criteria.PeriodSizePenalty; 031import org.cpsolver.exam.criteria.PerturbationPenalty; 032import org.cpsolver.exam.criteria.RoomPenalty; 033import org.cpsolver.exam.criteria.RoomPerturbationPenalty; 034import org.cpsolver.exam.criteria.RoomSizePenalty; 035import org.cpsolver.exam.criteria.RoomSplitDistancePenalty; 036import org.cpsolver.exam.criteria.RoomSplitPenalty; 037import org.cpsolver.exam.criteria.StudentBackToBackConflicts; 038import org.cpsolver.exam.criteria.StudentDirectConflicts; 039import org.cpsolver.exam.criteria.StudentDistanceBackToBackConflicts; 040import org.cpsolver.exam.criteria.StudentMoreThan2ADayConflicts; 041import org.cpsolver.exam.criteria.StudentNotAvailableConflicts; 042import org.cpsolver.exam.criteria.additional.DistanceToStronglyPreferredRoom; 043import org.cpsolver.exam.criteria.additional.DistributionViolation; 044import org.cpsolver.exam.criteria.additional.PeriodViolation; 045import org.cpsolver.exam.criteria.additional.RoomViolation; 046import org.cpsolver.exam.model.Exam; 047import org.cpsolver.exam.model.ExamInstructor; 048import org.cpsolver.exam.model.ExamModel; 049import org.cpsolver.exam.model.ExamPlacement; 050import org.cpsolver.exam.model.ExamStudent; 051import org.cpsolver.exam.reports.ExamAssignments; 052import org.cpsolver.exam.reports.ExamCourseSectionAssignments; 053import org.cpsolver.exam.reports.ExamInstructorConflicts; 054import org.cpsolver.exam.reports.ExamNbrMeetingsPerDay; 055import org.cpsolver.exam.reports.ExamPeriodUsage; 056import org.cpsolver.exam.reports.ExamRoomSchedule; 057import org.cpsolver.exam.reports.ExamRoomSplit; 058import org.cpsolver.exam.reports.ExamStudentBackToBackConflicts; 059import org.cpsolver.exam.reports.ExamStudentConflicts; 060import org.cpsolver.exam.reports.ExamStudentConflictsBySectionCourse; 061import org.cpsolver.exam.reports.ExamStudentConflictsPerExam; 062import org.cpsolver.exam.reports.ExamStudentDirectConflicts; 063import org.cpsolver.exam.reports.ExamStudentMoreTwoADay; 064import org.cpsolver.exam.split.ExamSplitter; 065import org.cpsolver.ifs.assignment.Assignment; 066import org.cpsolver.ifs.assignment.DefaultParallelAssignment; 067import org.cpsolver.ifs.assignment.DefaultSingleAssignment; 068import org.cpsolver.ifs.solution.Solution; 069import org.cpsolver.ifs.solution.SolutionListener; 070import org.cpsolver.ifs.solver.ParallelSolver; 071import org.cpsolver.ifs.solver.Solver; 072import org.cpsolver.ifs.util.DataProperties; 073import org.cpsolver.ifs.util.Progress; 074import org.cpsolver.ifs.util.ToolBox; 075import org.dom4j.Document; 076import org.dom4j.io.OutputFormat; 077import org.dom4j.io.SAXReader; 078import org.dom4j.io.XMLWriter; 079 080/** 081 * An examination timetabling test program. The following steps are performed: 082 * <ul> 083 * <li>Input properties are loaded 084 * <li>Input problem is loaded (General.Input property) 085 * <li>Problem is solved (using the given properties) 086 * <li>Solution is save (General.OutputFile property) 087 * </ul> 088 * <br> 089 * <br> 090 * Usage: 091 * <pre><code>java -Xmx1024m -jar examtt-1.1.jar exam.properties input.xml output.xml</code></pre> 092 * <br> 093 * 094 * @version ExamTT 1.3 (Examination Timetabling)<br> 095 * Copyright (C) 2007 - 2014 Tomáš Müller<br> 096 * <a href="mailto:muller@unitime.org">muller@unitime.org</a><br> 097 * <a href="http://muller.unitime.org">http://muller.unitime.org</a><br> 098 * <br> 099 * This library is free software; you can redistribute it and/or modify 100 * it under the terms of the GNU Lesser General Public License as 101 * published by the Free Software Foundation; either version 3 of the 102 * License, or (at your option) any later version. <br> 103 * <br> 104 * This library is distributed in the hope that it will be useful, but 105 * WITHOUT ANY WARRANTY; without even the implied warranty of 106 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 107 * Lesser General Public License for more details. <br> 108 * <br> 109 * You should have received a copy of the GNU Lesser General Public 110 * License along with this library; if not see 111 * <a href='http://www.gnu.org/licenses/'>http://www.gnu.org/licenses/</a>. 112 */ 113public class Test { 114 private static org.apache.log4j.Logger sLog = org.apache.log4j.Logger.getLogger(Test.class); 115 private static java.text.DecimalFormat sDoubleFormat = new java.text.DecimalFormat("0.00", new java.text.DecimalFormatSymbols(Locale.US)); 116 117 /** 118 * Setup log4j logging 119 * 120 * @param logFile 121 * log file 122 * @param debug 123 * true if debug messages should be logged (use -Ddebug=true to 124 * enable debug message) 125 */ 126 public static void setupLogging(File logFile, boolean debug) { 127 Logger root = Logger.getRootLogger(); 128 ConsoleAppender console = new ConsoleAppender(new PatternLayout("[%t] %m%n")); 129 console.setThreshold(Level.INFO); 130 root.addAppender(console); 131 if (logFile != null) { 132 try { 133 FileAppender file = new FileAppender(new PatternLayout( 134 "%d{dd-MMM-yy HH:mm:ss.SSS} [%t] %-5p %c{2}> %m%n"), logFile.getPath(), false); 135 file.setThreshold(Level.DEBUG); 136 root.addAppender(file); 137 } catch (IOException e) { 138 sLog.fatal("Unable to configure logging, reason: " + e.getMessage(), e); 139 } 140 } 141 if (!debug) 142 root.setLevel(Level.INFO); 143 } 144 145 /** Generate exam reports 146 * @param model problem model 147 * @param assignment current assignment 148 * @param outDir output folder 149 * @param outName output file name prefix 150 * @throws IOException may be thrown when writing fails 151 **/ 152 public static void createReports(ExamModel model, Assignment<Exam, ExamPlacement> assignment, File outDir, String outName) throws IOException { 153 new ExamAssignments(model).report(assignment).save(new File(outDir, outName + ".schdex.csv")); 154 155 new ExamCourseSectionAssignments(model).report(assignment).save(new File(outDir, outName + ".schdcs.csv")); 156 157 new ExamStudentConflicts(model).report(assignment).save(new File(outDir, outName + ".sconf.csv")); 158 159 new ExamInstructorConflicts(model).report(assignment).save(new File(outDir, outName + ".iconf.csv")); 160 161 new ExamStudentConflictsPerExam(model).report(assignment).save(new File(outDir, outName + ".sconfex.csv")); 162 163 new ExamStudentDirectConflicts(model).report(assignment).save(new File(outDir, outName + ".sdir.csv")); 164 165 new ExamStudentBackToBackConflicts(model).report(assignment).save(new File(outDir, outName + ".sbtb.csv")); 166 167 new ExamStudentMoreTwoADay(model).report(assignment).save(new File(outDir, outName + ".sm2d.csv")); 168 169 new ExamPeriodUsage(model).report(assignment).save(new File(outDir, outName + ".per.csv")); 170 171 new ExamRoomSchedule(model).report(assignment).save(new File(outDir, outName + ".schdr.csv")); 172 173 new ExamRoomSplit(model).report(assignment).save(new File(outDir, outName + ".rsplit.csv")); 174 175 new ExamNbrMeetingsPerDay(model).report(assignment).save(new File(outDir, outName + ".distmpd.csv")); 176 177 new ExamStudentConflictsBySectionCourse(model).report(assignment).save(new File(outDir, outName + ".sconfcs.csv")); 178 } 179 180 public static class ShutdownHook extends Thread { 181 Solver<Exam, ExamPlacement> iSolver = null; 182 183 public ShutdownHook(Solver<Exam, ExamPlacement> solver) { 184 setName("ShutdownHook"); 185 iSolver = solver; 186 } 187 188 @Override 189 public void run() { 190 try { 191 if (iSolver.isRunning()) 192 iSolver.stopSolver(); 193 Solution<Exam, ExamPlacement> solution = iSolver.lastSolution(); 194 Progress.removeInstance(solution.getModel()); 195 if (solution.getBestInfo() == null) { 196 sLog.error("No best solution found."); 197 } else 198 solution.restoreBest(); 199 200 sLog.info("Best solution:" + ToolBox.dict2string(solution.getExtendedInfo(), 1)); 201 202 sLog.info("Best solution found after " + solution.getBestTime() + " seconds (" 203 + solution.getBestIteration() + " iterations)."); 204 sLog.info("Number of assigned variables is " + solution.getAssignment().nrAssignedVariables()); 205 sLog.info("Total value of the solution is " + solution.getModel().getTotalValue(solution.getAssignment())); 206 207 File outFile = new File(iSolver.getProperties().getProperty("General.OutputFile", 208 iSolver.getProperties().getProperty("General.Output") + File.separator + "solution.xml")); 209 FileOutputStream fos = new FileOutputStream(outFile); 210 (new XMLWriter(fos, OutputFormat.createPrettyPrint())).write(((ExamModel) solution.getModel()).save(solution.getAssignment())); 211 fos.flush(); 212 fos.close(); 213 214 if ("true".equals(System.getProperty("reports", "false"))) 215 createReports((ExamModel) solution.getModel(), solution.getAssignment(), outFile.getParentFile(), outFile.getName() 216 .substring(0, outFile.getName().lastIndexOf('.'))); 217 218 String baseName = new File(iSolver.getProperties().getProperty("General.Input")).getName(); 219 if (baseName.indexOf('.') > 0) 220 baseName = baseName.substring(0, baseName.lastIndexOf('.')); 221 addCSVLine(new File(outFile.getParentFile(), baseName + ".csv"), outFile.getName(), iSolver.getProperties().getProperty("General.Config"), solution); 222 223 } catch (Exception e) { 224 e.printStackTrace(); 225 } 226 } 227 } 228 229 private static void addCSVLine(File file, String instance, String config, Solution<Exam, ExamPlacement> solution) { 230 try { 231 ExamModel model = (ExamModel) solution.getModel(); 232 Assignment<Exam, ExamPlacement> assignment = solution.getAssignment(); 233 boolean ex = file.exists(); 234 PrintWriter pw = new PrintWriter(new FileWriter(file, true)); 235 boolean mpp = ((PerturbationPenalty)model.getCriterion(PerturbationPenalty.class)).isMPP(); 236 int largeSize = ((LargeExamsPenalty)model.getCriterion(LargeExamsPenalty.class)).getLargeSize(); 237 RoomSplitDistancePenalty splitDistance = (RoomSplitDistancePenalty)model.getCriterion(RoomSplitDistancePenalty.class); 238 ExamSplitter splitter = (ExamSplitter)model.getCriterion(ExamSplitter.class); 239 PeriodViolation violPer = (PeriodViolation)model.getCriterion(PeriodViolation.class); 240 RoomViolation violRoom = (RoomViolation)model.getCriterion(RoomViolation.class); 241 DistributionViolation violDist = (DistributionViolation)model.getCriterion(DistributionViolation.class); 242 DistanceToStronglyPreferredRoom distStrPref = (DistanceToStronglyPreferredRoom)model.getCriterion(DistanceToStronglyPreferredRoom.class); 243 ExamRotationPenalty rotation = (ExamRotationPenalty)model.getCriterion(ExamRotationPenalty.class); 244 DecimalFormat df = new DecimalFormat("0.00"); 245 if (!ex) { 246 pw.println("SEED" 247 + ",NA,DC,M2D,BTB" + (model.getBackToBackDistance() < 0 ? "" : ",dBTB") 248 + ",iNA,iDC,iM2D,iBTB" + (model.getBackToBackDistance() < 0 ? "" : ",diBTB") 249 + ",PP,RP,DP" 250 + ",PI,@P,PS" // Period Index, Rotation Penalty, Period Size 251 + ",RSz,RSp,RD" // Room Size, Room Split, Room Split Distance 252 + (largeSize >= 0 ? ",LP" : "") 253 + (mpp ? ",IP,IRP" : "") 254 + (distStrPref == null ? "" : ",@D") 255 + (splitter == null ? "" : ",XX") 256 + (violPer == null ? "" : ",!P") 257 + (violRoom == null ? "" : ",!R") 258 + (violDist == null ? "" : ",!D") 259 + ",INSTANCE,CONFIG"); 260 int nrStudentExams = 0; 261 for (ExamStudent student : model.getStudents()) { 262 nrStudentExams += student.variables().size(); 263 } 264 int nrInstructorExams = 0; 265 for (ExamInstructor instructor : model.getInstructors()) { 266 nrInstructorExams += instructor.variables().size(); 267 } 268 pw.println("MIN" 269 + ",#EX,#RM,#PER," + (model.getBackToBackDistance() < 0 ? "" : ",") 270 + ",#STD,#STDX,#INS,#INSX" + (model.getBackToBackDistance() < 0 ? "" : ",") 271 + "," + model.getCriterion(PeriodPenalty.class).getBounds(assignment)[0] 272 + "," + model.getCriterion(RoomPenalty.class).getBounds(assignment)[0] 273 + "," + model.getCriterion(DistributionPenalty.class).getBounds(assignment)[0] 274 + ",," + df.format(rotation.averagePeriod(assignment)) + "," 275 + ",,," 276 + (largeSize >= 0 ? ",0" : "") 277 + (mpp ? ",," : "") 278 + (distStrPref == null ? "" : ",") 279 + (splitter == null ? "" : ",") 280 + (violPer == null ? "" : ",") 281 + (violRoom == null ? "" : ",") 282 + (violDist == null ? "" : ",") 283 + ",,"); 284 pw.println("MAX" 285 + "," + model.variables().size() + "," + model.getRooms().size() + "," + model.getPeriods().size() + "," + (model.getBackToBackDistance() < 0 ? "" : ",") 286 + "," + model.getStudents().size() + "," + nrStudentExams + "," + model.getInstructors().size() + "," + nrInstructorExams + (model.getBackToBackDistance() < 0 ? "" : ",") 287 + "," + model.getCriterion(PeriodPenalty.class).getBounds(assignment)[1] 288 + "," + model.getCriterion(RoomPenalty.class).getBounds(assignment)[1] 289 + "," + model.getCriterion(DistributionPenalty.class).getBounds(assignment)[1] 290 + ",," + rotation.nrAssignedExamsWithAvgPeriod(assignment) + "," 291 + ",,," 292 + (largeSize >= 0 ? "," + model.getCriterion(LargeExamsPenalty.class).getBounds(assignment)[1] : "") 293 + (mpp ? ",," : "") 294 + (distStrPref == null ? "" : ",") 295 + (splitter == null ? "" : ",") 296 + (violPer == null ? "" : "," + model.getCriterion(PeriodViolation.class).getBounds(assignment)[1]) 297 + (violRoom == null ? "" : "," + model.getCriterion(RoomViolation.class).getBounds(assignment)[1]) 298 + (violDist == null ? "" : "," + model.getCriterion(DistributionViolation.class).getBounds(assignment)[1]) 299 + ",,"); 300 } 301 pw.println(ToolBox.getSeed() 302 + "," + model.getCriterion(StudentNotAvailableConflicts.class).getValue(assignment) 303 + "," + model.getCriterion(StudentDirectConflicts.class).getValue(assignment) 304 + "," + model.getCriterion(StudentMoreThan2ADayConflicts.class).getValue(assignment) 305 + "," + model.getCriterion(StudentBackToBackConflicts.class).getValue(assignment) 306 + (model.getBackToBackDistance() < 0 ? "" : "," + model.getCriterion(StudentDistanceBackToBackConflicts.class).getValue(assignment)) 307 + "," + model.getCriterion(InstructorNotAvailableConflicts.class).getValue(assignment) 308 + "," + model.getCriterion(InstructorDirectConflicts.class).getValue(assignment) 309 + "," + model.getCriterion(InstructorMoreThan2ADayConflicts.class).getValue(assignment) 310 + "," + model.getCriterion(InstructorBackToBackConflicts.class).getValue(assignment) 311 + (model.getBackToBackDistance() < 0 ? "" : "," + model.getCriterion(InstructorDistanceBackToBackConflicts.class).getValue(assignment)) 312 + "," + model.getCriterion(PeriodPenalty.class).getValue(assignment) 313 + "," + model.getCriterion(RoomPenalty.class).getValue(assignment) 314 + "," + model.getCriterion(DistributionPenalty.class).getValue(assignment) 315 + "," + df.format(model.getCriterion(PeriodIndexPenalty.class).getValue(assignment) / assignment.nrAssignedVariables()) 316 + "," + df.format(Math.sqrt(rotation.getValue(assignment) / rotation.nrAssignedExamsWithAvgPeriod(assignment)) - 1) 317 + "," + df.format(model.getCriterion(PeriodSizePenalty.class).getValue(assignment) / assignment.nrAssignedVariables()) 318 + "," + df.format(model.getCriterion(RoomSizePenalty.class).getValue(assignment) / assignment.nrAssignedVariables()) 319 + "," + model.getCriterion(RoomSplitPenalty.class).getValue(assignment) 320 + "," + df.format(splitDistance.nrRoomSplits(assignment) <= 0 ? 0.0 : splitDistance.getValue(assignment) / splitDistance.nrRoomSplits(assignment)) 321 + (largeSize >= 0 ? "," + model.getCriterion(LargeExamsPenalty.class).getValue(assignment) : "") 322 + (mpp ? "," + df.format(model.getCriterion(PerturbationPenalty.class).getValue(assignment) / assignment.nrAssignedVariables()) 323 + "," + df.format(model.getCriterion(RoomPerturbationPenalty.class).getValue(assignment) / assignment.nrAssignedVariables()): "") 324 + (distStrPref == null ? "" : "," + df.format(distStrPref.getValue(assignment) / assignment.nrAssignedVariables())) 325 + (splitter == null ? "" : "," + df.format(splitter.getValue(assignment))) 326 + (violPer == null ? "" : "," + df.format(violPer.getValue(assignment))) 327 + (violRoom == null ? "" : "," + df.format(violRoom.getValue(assignment))) 328 + (violDist == null ? "" : "," + df.format(violDist.getValue(assignment))) 329 + "," + instance + "," + config); 330 pw.flush(); 331 pw.close(); 332 } catch (Exception e) { 333 sLog.error("Unable to add CSV line to " + file, e); 334 } 335 } 336 337 /** 338 * Main program 339 * 340 * @param args 341 * problem property file, input file (optional, may be set by 342 * General.Input property), output file (optional, may be set by 343 * General.OutputFile property) 344 */ 345 public static void main(String[] args) { 346 try { 347 DataProperties cfg = new DataProperties(); 348 cfg.setProperty("Termination.StopWhenComplete", "false"); 349 cfg.setProperty("Termination.TimeOut", "1800"); 350 cfg.setProperty("General.SaveBestUnassigned", "-1"); 351 cfg.setProperty("Neighbour.Class", "org.cpsolver.exam.heuristics.ExamNeighbourSelection"); 352 if (args.length >= 1) { 353 cfg.load(new FileInputStream(args[0])); 354 cfg.setProperty("General.Config", new File(args[0]).getName()); 355 } 356 cfg.putAll(System.getProperties()); 357 358 File inputFile = new File("c:\\test\\exam\\exam1070.xml"); 359 if (args.length >= 2) { 360 inputFile = new File(args[1]); 361 } 362 ToolBox.setSeed(cfg.getPropertyLong("General.Seed", Math.round(Long.MAX_VALUE * Math.random()))); 363 364 cfg.setProperty("General.Input", inputFile.toString()); 365 366 String outName = inputFile.getName(); 367 if (outName.indexOf('.') >= 0) 368 outName = outName.substring(0, outName.lastIndexOf('.')) + "s.xml"; 369 File outFile = new File(inputFile.getParentFile(), outName); 370 if (args.length >= 3) { 371 outFile = new File(args[2]); 372 if (outFile.exists() && outFile.isDirectory()) 373 outFile = new File(outFile, outName); 374 if (!outFile.exists() && !outFile.getName().endsWith(".xml")) 375 outFile = new File(outFile, outName); 376 } 377 if (outFile.getParentFile() != null) 378 outFile.getParentFile().mkdirs(); 379 cfg.setProperty("General.OutputFile", outFile.toString()); 380 cfg.setProperty("General.Output", outFile.getParent()); 381 382 String logName = outFile.getName(); 383 if (logName.indexOf('.') >= 0) 384 logName = logName.substring(0, logName.lastIndexOf('.')) + ".log"; 385 setupLogging(new File(outFile.getParent(), logName), "true".equals(System.getProperty("debug", "false"))); 386 387 ExamModel model = new ExamModel(cfg); 388 389 Document document = (new SAXReader()).read(new File(cfg.getProperty("General.Input"))); 390 int nrSolvers = cfg.getPropertyInt("Parallel.NrSolvers", 1); 391 Assignment<Exam, ExamPlacement> assignment = (nrSolvers <= 1 ? new DefaultSingleAssignment<Exam, ExamPlacement>() : new DefaultParallelAssignment<Exam, ExamPlacement>()); 392 model.load(document, assignment); 393 394 Solver<Exam, ExamPlacement> solver = (nrSolvers == 1 ? new Solver<Exam, ExamPlacement>(cfg) : new ParallelSolver<Exam, ExamPlacement>(cfg)); 395 solver.setInitalSolution(new Solution<Exam, ExamPlacement>(model, assignment)); 396 397 solver.currentSolution().addSolutionListener(new SolutionListener<Exam, ExamPlacement>() { 398 @Override 399 public void solutionUpdated(Solution<Exam, ExamPlacement> solution) { 400 } 401 402 @Override 403 public void getInfo(Solution<Exam, ExamPlacement> solution, Map<String, String> info) { 404 } 405 406 @Override 407 public void getInfo(Solution<Exam, ExamPlacement> solution, Map<String, String> info, 408 Collection<Exam> variables) { 409 } 410 411 @Override 412 public void bestCleared(Solution<Exam, ExamPlacement> solution) { 413 } 414 415 @Override 416 public void bestSaved(Solution<Exam, ExamPlacement> solution) { 417 ExamModel m = (ExamModel) solution.getModel(); 418 Assignment<Exam, ExamPlacement> a = solution.getAssignment(); 419 if (sLog.isInfoEnabled()) { 420 sLog.info("**BEST[" + solution.getIteration() + "]** " 421 + (m.variables().size() > a.nrAssignedVariables() ? "V:" + a.nrAssignedVariables() + "/" + m.variables().size() + " - " : "") + 422 "T:" + new DecimalFormat("0.00").format(m.getTotalValue(a)) + " " + m.toString(a) + 423 (solution.getFailedIterations() > 0 ? ", F:" + sDoubleFormat.format(100.0 * solution.getFailedIterations() / solution.getIteration()) + "%" : "")); 424 } 425 } 426 427 @Override 428 public void bestRestored(Solution<Exam, ExamPlacement> solution) { 429 } 430 }); 431 432 Runtime.getRuntime().addShutdownHook(new ShutdownHook(solver)); 433 434 solver.start(); 435 try { 436 solver.getSolverThread().join(); 437 } catch (InterruptedException e) { 438 } 439 } catch (Exception e) { 440 e.printStackTrace(); 441 } 442 } 443}