Java tutorial
/* * Copyright "Open Digital Education", 2016 * * This program is published by "Open Digital Education". * You must indicate the name of the software and the company in any production /contribution * using the software and indicate on the home page of the software industry in question, * "powered by Open Digital Education" with a reference to the website: https://opendigitaleducation.com/. * * This program is free software, licensed under the terms of the GNU Affero General Public License * as published by the Free Software Foundation, version 3 of the License. * * You can redistribute this application and/or modify it since you respect the terms of the GNU Affero General Public License. * If you modify the source code and then use this modified source code in your creation, you must make available the source code of your modifications. * * You should have received a copy of the GNU Affero General Public License along with the software. * If not, please see : <http://www.gnu.org/licenses/>. Full compliance requires reading the terms of this license and following its directives. */ package org.entcore.feeder.timetable.udt; import fr.wseduc.swift.storage.DefaultAsyncResult; import fr.wseduc.webutils.I18n; import fr.wseduc.webutils.Utils; import org.entcore.common.neo4j.Neo4jUtils; import org.entcore.common.utils.FileUtils; import org.entcore.common.validation.StringValidation; import org.entcore.feeder.dictionary.structures.PostImport; import org.entcore.feeder.timetable.AbstractTimetableImporter; import org.entcore.feeder.timetable.Slot; import org.entcore.feeder.utils.JsonUtil; import org.entcore.feeder.utils.Report; import org.entcore.feeder.utils.Validator; import org.joda.time.DateTime; import org.joda.time.format.DateTimeFormat; import io.vertx.core.AsyncResult; import io.vertx.core.Handler; import io.vertx.core.Vertx; import io.vertx.core.eventbus.Message; import io.vertx.core.json.JsonArray; import io.vertx.core.json.JsonObject; import org.xml.sax.InputSource; import org.xml.sax.XMLReader; import org.xml.sax.helpers.XMLReaderFactory; import java.io.File; import java.io.FileInputStream; import java.security.NoSuchAlgorithmException; import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; import static fr.wseduc.webutils.Utils.getOrElse; import static fr.wseduc.webutils.Utils.isNotEmpty; import static org.entcore.common.utils.StringUtils.isEmpty; import static org.entcore.common.utils.StringUtils.padLeft; import static org.entcore.feeder.dictionary.structures.DefaultProfiles.TEACHER_PROFILE_EXTERNAL_ID; public class UDTImporter extends AbstractTimetableImporter { private static final String STUDENTS_TO_GROUPS = "MATCH (:Structure {externalId:{structureExternalId}})<-[:DEPENDS]-(:ProfileGroup)<-[:IN]-(u:User), " + "(fg:FunctionalGroup {externalId:{externalId}}) " + "WHERE lower(u.firstName) = {firstName} AND lower(u.lastName) = {lastName} AND u.birthDate = {birthDate} " + "MERGE u-[r:IN]->fg " + "SET r.lastUpdated = {now}, r.source = {source}, r.inDate = {inDate}, r.outDate = {outDate} "; private static final String PERSIST_USED_GROUPS = "MATCH (:Structure {externalId:{structureExternalId}})<-[:DEPENDS]-(fg:FunctionalGroup) " + "WHERE fg.idgpe IN {usedGroups} " + "SET fg.usedInCourses = true "; public static final String UDT = "UDT"; public static final String CODE = "code"; private final Pattern filenameWeekPatter; public static final String DATE_FORMAT = "dd/MM/yyyy"; private int year; private long endStudents; private Map<String, Set<String>> coens = new HashMap<>(); private Map<String, JsonObject> fichesT = new HashMap<>(); private Map<String, String> regroup = new HashMap<>(); private Map<String, List<JsonObject>> lfts = new HashMap<>(); private HashMap<Integer, Integer> periods = new HashMap<>(); // key : start, value : end period private int maxYearWeek; private Vertx vertx; private DateTime startDateStudents; private Set<DateTime> holidays = new HashSet<>(); private Set<Integer> holidaysWeeks = new HashSet<>(); private Map<String, JsonObject> eleves = new HashMap<>(); private Map<String, String> codeGepDiv = new HashMap<>(); private Set<String> usedGroupInCourses = new HashSet<>(); private final boolean authorizeUserCreation; private final boolean udcalLowerCase; public UDTImporter(Vertx vertx, String uai, String path, String acceptLanguage, boolean authorizeUserCreation) { super(uai, path, acceptLanguage); this.vertx = vertx; this.authorizeUserCreation = authorizeUserCreation; udcalLowerCase = vertx.fileSystem().existsBlocking(basePath + "udcal_24.xml"); if (udcalLowerCase) { filenameWeekPatter = Pattern.compile("udcal_[0-9]{2}_([0-9]{2})\\.xml$"); } else { filenameWeekPatter = Pattern.compile("UDCal_[0-9]{2}_([0-9]{2})\\.xml$"); } } @Override public void launch(final Handler<AsyncResult<Report>> handler) throws Exception { init(new Handler<AsyncResult<Void>>() { @Override public void handle(AsyncResult<Void> event) { if (event.failed()) { handler.handle(new DefaultAsyncResult<Report>(event.cause())); return; } try { parse(basePath + "UDCal_24.xml"); parse(basePath + "UDCal_00.xml"); parse(basePath + "semaines.xml"); parse(basePath + "UDCal_03.xml"); parse(basePath + "UDCal_04.xml"); parse(basePath + "UDCal_05.xml"); parse(basePath + "UDCal_07.xml"); parse(basePath + "UDCal_08.xml"); parse(basePath + "UDCal_10.xml"); parse(basePath + "UDCal_19.xml"); parse(basePath + "UDCal_13.xml"); parse(basePath + "UDCal_21.xml"); parse(basePath + "UDCal_23.xml"); parse(basePath + "UDCal_11.xml"); parse(basePath + "UDCal_12.xml"); generateCourses(startDateWeek1.getWeekOfWeekyear(), true); final String UCal12Filter = udcalLowerCase ? "udcal_12_[0-9]+.xml" : "UDCal_12_[0-9]+.xml"; vertx.fileSystem().readDir(basePath, UCal12Filter, new Handler<AsyncResult<List<String>>>() { @Override public void handle(AsyncResult<List<String>> event) { if (event.succeeded()) { try { for (String p : event.result()) { Matcher m = filenameWeekPatter.matcher(p); if (m.find()) { final int weekNumber = Integer.parseInt(m.group(1)); if (periods.containsKey(weekNumber)) { parse(p); generateCourses(weekNumber, false); } else { log.warn("Ignore week : " + weekNumber); } } } persistUsedGroups(); commit(handler); } catch (Exception e) { handler.handle(new DefaultAsyncResult<Report>(e)); } } } }); } catch (Exception e) { handler.handle(new DefaultAsyncResult<Report>(e)); } } }); } private void parse(String filePath) throws Exception { if (udcalLowerCase) { filePath = filePath.toLowerCase(); } InputSource in = new InputSource(new FileInputStream(filePath)); UDTHandler sh = new UDTHandler(this); XMLReader xr = XMLReaderFactory.createXMLReader(); xr.setContentHandler(sh); xr.parse(in); } void setYear(String year) { if (this.year == 0) { this.year = Integer.parseInt(year); } } public void setEndStudents(String endStudents) { this.endStudents = DateTime.parse(endStudents, DateTimeFormat.forPattern(DATE_FORMAT)).getMillis(); } public void setStartDateStudents(String startDateStudents) { this.startDateStudents = DateTime.parse(startDateStudents, DateTimeFormat.forPattern(DATE_FORMAT)); maxYearWeek = this.startDateStudents.weekOfWeekyear().withMaximumValue().weekOfWeekyear().get(); } void initSchoolYear(JsonObject currentEntity) { startDateWeek1 = startDateStudents .withWeekOfWeekyear(Integer.parseInt(currentEntity.getString("premiere_semaine_ISO"))) .withDayOfWeek(1); slotDuration = Integer.parseInt(currentEntity.getString("duree_seq")) / 2; DateTime h = startDateWeek1; while (h.isBefore(startDateStudents)) { holidays.add(h); h = h.plusDays(1); holidaysWeeks.add(h.getWeekOfWeekyear()); } } void initSchedule(JsonObject e) { final String slotKey = e.getString("code_jour") + padLeft(e.getString(CODE), 2, '0') + e.getString("code_site"); Slot s = new Slot(e.getString("hre_deb"), e.getString("hre_fin"), slotDuration); slots.put(slotKey, s); } void initPeriods(JsonObject currentEntity) { JsonArray weeks = currentEntity.getJsonArray("semaine"); if (weeks != null && weeks.size() > 0) { int oldWeek = startDateWeek1.getWeekOfWeekyear(); for (Object o : weeks) { if (o instanceof JsonObject) { int week = Integer.parseInt(((JsonObject) o).getString("num")); if (week != oldWeek) { periods.put(oldWeek, (week == 1 ? maxYearWeek : week - 1)); oldWeek = week; } } } periods.put(oldWeek, new DateTime(endStudents).getWeekOfWeekyear()); for (int i = new DateTime(endStudents).getWeekOfWeekyear() + 1; i < startDateWeek1 .getWeekOfWeekyear(); i++) { holidaysWeeks.add(i); } } } void initHolidays(JsonObject currentEntity) { DateTime s = DateTime.parse(currentEntity.getString("debut"), DateTimeFormat.forPattern(DATE_FORMAT)); DateTime e = DateTime.parse(currentEntity.getString("fin"), DateTimeFormat.forPattern(DATE_FORMAT)); while (s.isBefore(e)) { holidays.add(s); s = s.plusDays(1); holidaysWeeks.add(s.getWeekOfWeekyear()); } holidays.add(e); } void addRoom(JsonObject currentEntity) { rooms.put(currentEntity.getString(CODE), currentEntity.getString("nom")); } @Override protected String getTeacherMappingAttribute() { return "externalId"; } @Override protected String getSource() { return UDT; } void addProfesseur(JsonObject currentEntity) { try { if (isEmpty(currentEntity.getString("code_matppl"))) { // Ignore prof without subject. // Often this case corresponds to personnel. return; } final String id = currentEntity.getString(CODE); String externalId = currentEntity.getString("epj"); final String firstName = currentEntity.getString("prenom"); final String lastName = currentEntity.getString("nom"); JsonObject p = persEducNat.applyMapping(currentEntity); p.put("profiles", new fr.wseduc.webutils.collections.JsonArray().add("Teacher")); if (isEmpty(externalId)) { externalId = JsonUtil.checksum(p, JsonUtil.HashAlgorithm.MD5); } p.put("externalId", externalId); userImportedExternalId.add(externalId); String[] teacherId = teachersMapping.get(externalId); if (teacherId == null) { teacherId = teachersCleanNameMapping.get(Validator.sanitize(firstName + lastName)); } if (teacherId != null && isNotEmpty(teacherId[0])) { teachers.put(id, teacherId[0]); if (getSource().equals(teacherId[1]) && authorizeUserCreation) { updateUser(p); } } else { final String userId = UUID.randomUUID().toString(); p.put("id", userId); p.put("structures", new JsonArray().add(structureExternalId)); if (authorizeUserCreation) { persEducNat.createOrUpdatePersonnel(p, TEACHER_PROFILE_EXTERNAL_ID, structure, null, null, true, true); } teachers.put(id, userId); } } catch (Exception e) { report.addError(e.getMessage()); } } void addSubject(JsonObject s) { final String code = s.getString(CODE); super.addSubject(code, new JsonObject().put("Code", code).put("Libelle", s.getString("libelle"))); } void addClasse(JsonObject currentEntity) { final String id = currentEntity.getString(CODE); classes.put(id, currentEntity); final String codeGep = currentEntity.getString("code_gep"); if (isNotEmpty(codeGep)) { codeGepDiv.put(id, codeGep); } final String className = (classesMapping != null) ? getOrElse(classesMapping.getString(id), id, false) : id; currentEntity.put("className", className); if (className != null) { txXDT.add(UNKNOWN_CLASSES, new JsonObject().put("UAI", UAI).put("className", className)); } } void addGroup(JsonObject currentEntity) { final String id = currentEntity.getString("code_div") + currentEntity.getString(CODE); groups.put(id, currentEntity); String name = currentEntity.getString("code_div") + " Gr " + currentEntity.getString(CODE); if (isEmpty(name)) { name = id; } currentEntity.put("code_gep", codeGepDiv.get(currentEntity.getString("code_div"))); currentEntity.put("idgpe", currentEntity.remove("id")); final String set = "SET " + Neo4jUtils.nodeSetPropertiesFromJson("fg", currentEntity); txXDT.add(CREATE_GROUPS + set, currentEntity.put("structureExternalId", structureExternalId).put("name", name) .put("displayNameSearchField", Validator.sanitize(name)) .put("externalId", structureExternalId + "$" + name).put("id", UUID.randomUUID().toString()) .put("source", getSource())); } void addGroup2(JsonObject currentEntity) { final String codeGroup = currentEntity.getString("code_gpe"); final String name = currentEntity.getString("nom"); if (isNotEmpty(codeGroup)) { final String groupId = currentEntity.getString("code_div") + codeGroup; JsonObject group = groups.get(groupId); if (group == null) { log.warn("addGroup2 : unknown.group.mapping"); return; } JsonArray groups = group.getJsonArray("groups"); if (groups == null) { groups = new fr.wseduc.webutils.collections.JsonArray(); group.put("groups", groups); } groups.add(name); } else { final String classId = currentEntity.getString("code_div"); JsonObject classe = classes.get(classId); if (classe == null) { log.warn("addGroup2 : unknown.class.mapping"); return; } JsonArray groups = classe.getJsonArray("groups"); if (groups == null) { groups = new fr.wseduc.webutils.collections.JsonArray(); classe.put("groups", groups); } groups.add(name); } regroup.put(currentEntity.getString(CODE), name); txXDT.add(CREATE_GROUPS + "SET fg.idrgpmt = {idrgpmt} ", new JsonObject().put("structureExternalId", structureExternalId).put("name", name) .put("displayNameSearchField", Validator.sanitize(name)) .put("externalId", structureExternalId + "$" + name).put("id", UUID.randomUUID().toString()) .put("source", getSource()).put("idrgpmt", currentEntity.getString("id"))); } private void persistUsedGroups() { txXDT.add(PERSIST_USED_GROUPS, new JsonObject().put("structureExternalId", structureExternalId) .put("usedGroups", new JsonArray(new ArrayList<>(usedGroupInCourses)))); } void eleveMapping(JsonObject currentEntity) { eleves.put(currentEntity.getString(CODE), currentEntity); } void addEleve(JsonObject currentEntity) { if ("0".equals(currentEntity.getString("theorique"))) { return; } final String ele = currentEntity.getString("ele"); if (isEmpty(ele)) { report.addErrorWithParams("invalid.epj", currentEntity.encode()); return; } JsonObject eleve = eleves.get(ele); if (eleve == null) { report.addErrorWithParams("missing.student", currentEntity.encode()); return; } final String codeGroup = currentEntity.getString("gpe"); final String codeDiv = currentEntity.getString("code_div"); JsonArray groups; if (isNotEmpty(codeGroup)) { JsonObject group = this.groups.get(codeDiv + codeGroup); if (group == null) { log.warn("addEleve : unknown.group.mapping"); return; } final String name = group.getString("code_div") + " Gr " + group.getString(CODE); txXDT.add(STUDENTS_TO_GROUPS, new JsonObject().put("firstName", eleve.getString("prenom", "").toLowerCase()) .put("lastName", eleve.getString("nom", "").toLowerCase()) .put("birthDate", StringValidation.convertDate(eleve.getString("naissance", ""))) .put("externalId", structureExternalId + "$" + name) .put("structureExternalId", structureExternalId).put("source", UDT) .put("inDate", importTimestamp).put("outDate", endStudents) .put("now", importTimestamp)); groups = group.getJsonArray("groups"); } else { JsonObject classe = classes.get(codeDiv); if (classe == null) { log.warn("addEleve : unknown.class.mapping"); return; } groups = classe.getJsonArray("groups"); } if (groups != null) { for (Object o2 : groups) { txXDT.add(STUDENTS_TO_GROUPS, new JsonObject().put("firstName", eleve.getString("prenom", "").toLowerCase()) .put("lastName", eleve.getString("nom", "").toLowerCase()) .put("birthDate", StringValidation.convertDate(eleve.getString("naissance", ""))) .put("externalId", structureExternalId + "$" + o2.toString()) .put("structureExternalId", structureExternalId).put("source", UDT) .put("inDate", importTimestamp).put("outDate", endStudents) .put("now", importTimestamp)); } } } void addCoens(JsonObject currentEntity) { final String clf = currentEntity.getString("lignefic"); Set<String> teachers = coens.get(clf); if (teachers == null) { teachers = new HashSet<>(); coens.put(clf, teachers); } final String externalId = currentEntity.getString("epj"); String[] teacherId = null; if (isNotEmpty(externalId)) { teacherId = teachersMapping.get(externalId); } if (teacherId == null || isEmpty(teacherId[0])) { teacherId = new String[] { this.teachers.get(currentEntity.getString("prof")), getSource() }; } if (teacherId != null && isNotEmpty(teacherId[0])) { teachers.add(teacherId[0]); } } void addFicheT(JsonObject currentEntity) { final String id = currentEntity.getString(CODE); fichesT.put(id, currentEntity); } void addCourse(JsonObject entity) { final String div = entity.getString("div"); if (isEmpty(div)) { return; } final String fic = entity.getString("fic"); if (isEmpty(fic)) { report.addError("invalid.fic"); return; } final String idgpe = entity.getString("idgpe"); if ("0".equals(entity.getString("rgpmt")) && isNotEmpty(idgpe) && isNotEmpty(entity.getString("gpe"))) { usedGroupInCourses.add(idgpe); } final String tmpId = calculateTmpId(entity); List<JsonObject> l = lfts.get(tmpId); if (l == null) { l = new ArrayList<>(); lfts.put(tmpId, l); } l.add(entity); } private String calculateTmpId(JsonObject entity) { return entity.getString("div") + entity.getString("mat") + entity.getString("prof") + entity.getString("rgpmt") + getOrElse(entity.getString("gpe"), "_", false); } private void generateCourses(int periodWeek, boolean theoretical) { for (Map.Entry<Integer, Integer> e : getNextHolidaysWeek(periodWeek).entrySet()) { for (List<JsonObject> c : lfts.values()) { Collections.sort(c, new LftComparator()); String start = null; int current = 0; JsonObject previous = null; int count = 0; for (JsonObject j : c) { int val = Integer.parseInt(j.getString(CODE).substring(0, 3)); count++; if (start == null) { start = j.getString("fic"); current = val; } else if ((++current) != val || count == c.size()) { persistCourse(generateCourse(start, previous.getString("fic"), previous, e.getKey(), e.getValue(), theoretical)); start = j.getString("fic"); current = val; } previous = j; } } } lfts.clear(); } private Map<Integer, Integer> getNextHolidaysWeek(int periodWeek) { int endPeriod = periods.get(periodWeek); int e = (endPeriod < startDateWeek1.getWeekOfWeekyear()) ? endPeriod + maxYearWeek : endPeriod; int startPeriod = periodWeek; Map<Integer, Integer> p = new HashMap<>(); for (int i = periodWeek; i < e; i++) { int j = (i > maxYearWeek) ? i - maxYearWeek : i; if (startPeriod < 1 && !holidaysWeeks.contains(j)) { startPeriod = j; } if (startPeriod > 0 && (holidaysWeeks.contains(j) && startPeriod != j)) { p.put(startPeriod, j); startPeriod = 0; } } if (startPeriod > 0) { p.put(startPeriod, endPeriod); } return p; } private JsonObject generateCourse(String start, String end, JsonObject entity, int periodWeek, int endPeriodWeek, boolean theoretical) { JsonObject ficheTStart = fichesT.get(start); JsonObject ficheTEnd = fichesT.get(end); if (ficheTStart == null || ficheTEnd == null) { report.addError("invalid.ficheT"); return null; } final Slot slotStart = slots.get(ficheTStart.getString("jour") + padLeft(ficheTStart.getString("demi_seq"), 2, '0') + ficheTStart.getString("site")); final Slot slotEnd = slots.get(ficheTEnd.getString("jour") + padLeft(ficheTEnd.getString("demi_seq"), 2, '0') + ficheTEnd.getString("site")); if (slotStart == null || slotEnd == null) { report.addError("invalid.slot"); return null; } final int day = Integer.parseInt(ficheTStart.getString("jour")); final int cpw = (periodWeek < startDateWeek1.getWeekOfWeekyear()) ? periodWeek + maxYearWeek : periodWeek; DateTime startDate = startDateWeek1.plusWeeks(cpw - startDateWeek1.getWeekOfWeekyear()).plusDays(day - 1); while (holidays.contains(startDate)) { startDate = startDate.plusWeeks(1); } startDate = startDate.plusSeconds(slotStart.getStart()); //final int epw = periods.get(periodWeek); final int cepw = (endPeriodWeek < startDateWeek1.getWeekOfWeekyear()) ? endPeriodWeek + maxYearWeek : endPeriodWeek; DateTime endDate = startDateWeek1.plusWeeks(cepw - startDateWeek1.getWeekOfWeekyear()).plusDays(day - 1); while (holidays.contains(endDate)) { endDate = endDate.minusWeeks(1); } endDate = endDate.plusSeconds(slotEnd.getEnd()); if (endDate.isBefore(startDate)) { log.error("endDate before start date. cpw : " + cpw + ", cepw : " + cepw + ", startDateWeek1 : " + startDateWeek1 + ", endPeriodOfWeek : " + endPeriodWeek); } final Set<String> ce = coens.get(start); JsonArray teacherIds; if (ce != null && ce.size() > 0) { teacherIds = new fr.wseduc.webutils.collections.JsonArray(new ArrayList<>(ce)); } else { teacherIds = new fr.wseduc.webutils.collections.JsonArray(); } final String pId = teachers.get(entity.getString("prof")); if (isNotEmpty(pId)) { teacherIds.add(pId); } final JsonObject c = new JsonObject().put("structureId", structureId).put("startDate", startDate.toString()) .put("endDate", endDate.toString()).put("dayOfWeek", day).put("teacherIds", teacherIds) .put("theoretical", theoretical); final String sId = subjects.get(entity.getString("mat")); if (isNotEmpty(sId)) { c.put("subjectId", sId); } final String rId = rooms.get(entity.getString("salle")); if (isNotEmpty(rId)) { c.put("roomLabels", new fr.wseduc.webutils.collections.JsonArray().add(rId)); } final JsonObject cId = classes.get(entity.getString("div")); if (cId != null && isNotEmpty(cId.getString("className"))) { c.put("classes", new fr.wseduc.webutils.collections.JsonArray().add(cId.getString("className"))); } JsonArray groups; if (isNotEmpty(entity.getString("rgpmt")) || isNotEmpty(entity.getString("gpe"))) { groups = new fr.wseduc.webutils.collections.JsonArray(); c.put("groups", groups); String name = regroup.get(entity.getString("rgpmt")); if (isNotEmpty(name)) { groups.add(name); } String gName = entity.getString("gpe"); if (isNotEmpty(gName)) { groups.add(entity.getString("div") + " Gr " + gName); } } try { c.put("_id", JsonUtil.checksum(c)); } catch (NoSuchAlgorithmException e) { log.error("Error generating course checksum", e); } return c; } private class LftComparator implements Comparator<JsonObject> { @Override public int compare(JsonObject o1, JsonObject o2) { return o1.getString(CODE).compareTo(o2.getString(CODE)); } } public static void launchImport(Vertx vertx, final Message<JsonObject> message, boolean udtUserCreation) { launchImport(vertx, message, null, udtUserCreation); } public static void launchImport(Vertx vertx, final Message<JsonObject> message, final PostImport postImport, boolean udtUserCreation) { final I18n i18n = I18n.getInstance(); final String uai = message.body().getString("UAI"); final String path = message.body().getString("path"); final String acceptLanguage = message.body().getString("language", "fr"); if (Utils.isEmpty(uai) || Utils.isEmpty(path) || Utils.isEmpty(acceptLanguage)) { JsonObject json = new JsonObject().put("status", "error").put("message", i18n.translate("invalid.params", I18n.DEFAULT_DOMAIN, acceptLanguage)); message.reply(json); } try { final String parentPath = FileUtils.getParentPath(path); FileUtils.unzip(path, parentPath); new UDTImporter(vertx, uai, parentPath + File.separator, acceptLanguage, udtUserCreation) .launch(new Handler<AsyncResult<Report>>() { @Override public void handle(AsyncResult<Report> event) { if (event.succeeded()) { message.reply(new JsonObject().put("status", "ok").put("result", event.result().getResult())); if (postImport != null) { postImport.execute(); } } else { log.error(event.cause().getMessage(), event.cause()); JsonObject json = new JsonObject().put("status", "error").put("message", i18n.translate(event.cause().getMessage(), I18n.DEFAULT_DOMAIN, acceptLanguage)); message.reply(json); } } }); } catch (Exception e) { log.error(e.getMessage(), e); JsonObject json = new JsonObject().put("status", "error").put("message", i18n.translate(e.getMessage(), I18n.DEFAULT_DOMAIN, acceptLanguage)); message.reply(json); } } }