From 37d5cde945396322fec62ac45e750efba5ee947c Mon Sep 17 00:00:00 2001 From: =?utf8?q?V=C3=A1s=C3=A1ry=20D=C3=A1niel?= Date: Fri, 23 Feb 2018 16:35:13 +0000 Subject: [PATCH] git-tfs-id: [http://tfs.userrendszerhaz.hu:8080/tfs/DefaultCollection]$/MediaCube;C30922 --- server/-configuration/scheduledjobs.json | 44 ++- .../config/scheduledjobs.json | 43 ++- .../jobtemplates/archive-recording.xml | 56 ++++ .../import-morpheus-missing-materials.xml | 3 + .../jobtemplates/test-multiparam.xml | 43 +++ .../CopyForArchiveNEXIORecordingsStep.java | 3 +- .../server/steps/MetadataTransformStep.java | 52 +++- .../server/steps/MultiParamStep.java | 15 + .../server/steps/OctopusDataMiner.java | 32 +- .../RecordingsArchiveItemBuilderStep.java | 281 ++++++++++++++++++ .../steps/UploadRecordingToNexioStep.java | 141 +++++++++ .../src/user/commons/StoreUri.java | 16 +- .../src/user/commons/octopus/IOctopusAPI.java | 1 + .../src/user/commons/octopus/OctopusAPI.java | 18 +- .../octopus/test/OctopusDataMinerTest.java | 73 +++++ .../sql/2-CreateStructure.db2 | 2 +- .../sql/stat-details.sql | 229 +++++++++++++- server/user.jobengine.osgi.db/sql/stat-rd.sql | 14 + .../src/user/jobengine/server/JobRuntime.java | 12 +- .../jobengine/server/ast/InputParameter.java | 22 +- .../jobengine/server/steps/EscortFiles.java | 2 +- .../user/jobengine/server/steps/JobStep.java | 1 + .../user/jobengine/server/ast/ParserTest.java | 193 +++++------- 23 files changed, 1093 insertions(+), 203 deletions(-) create mode 100644 server/user.jobengine.executors/jobtemplates/archive-recording.xml create mode 100644 server/user.jobengine.executors/jobtemplates/test-multiparam.xml create mode 100644 server/user.jobengine.executors/src/user/jobengine/server/steps/MultiParamStep.java create mode 100644 server/user.jobengine.executors/src/user/jobengine/server/steps/RecordingsArchiveItemBuilderStep.java create mode 100644 server/user.jobengine.executors/src/user/jobengine/server/steps/UploadRecordingToNexioStep.java create mode 100644 server/user.jobengine.osgi.db/sql/stat-rd.sql diff --git a/server/-configuration/scheduledjobs.json b/server/-configuration/scheduledjobs.json index 16274c8a..076f1c8b 100644 --- a/server/-configuration/scheduledjobs.json +++ b/server/-configuration/scheduledjobs.json @@ -1,9 +1,33 @@ {"joblist":[ { "active": false, + "executeimmediate": false, + "name" : "Multiparam", + "template": "test-multiparam.xml", + "cronexpression": "0 0 0/1 1/1 * ? *", + "parameters": [ + {"name": "p1", "value": 1, "type": "java.lang.Long"}, + {"name": "p2", "value": "x", "type": "java.lang.String"} + ] + }, + { + "active": false, + "executeimmediate": false, + "name" : "Visszarögzített anyagok utólagos archiválása", + "template": "archive-recording.xml", + "cronexpression": "0 0 0/1 1/1 * ? *", + "parameters": [ + {"name": "sourcePath", "value": "\\\\10.10.254.74\\temp_isilon\\NEXIO", "type": "java.lang.String"}, + {"name": "nexioPort", "value": 2098, "type": "java.lang.Integer"}, + {"name": "nexioUserName", "value": "administrator", "type": "java.lang.String"}, + {"name": "nexioPassword", "value": "system", "type": "java.lang.String"} + ] + }, + { + "active": false, + "executeimmediate": false, "name" : "Híranyag statisztika importálása", "template": "import-statistics.xml", - "executeimmediate": false, "cronexpression": "0 0 0/1 1/1 * ? *", "parameters": [ {"name": "daysBeforeNow", "value": 1, "type": "java.lang.Integer"} @@ -11,9 +35,9 @@ }, { "active": false, + "executeimmediate": false, "name" : "MORPHEUS 'missing materials' importálása", "template": "import-morpheus-missing-materials.xml", - "executeimmediate": false, "cronexpression": "0 0 0/1 1/1 * ? *", "parameters": [ {"name": "csvFilePath", "value": "/mnt/MORPHEUS", "type": "java.lang.String"}, @@ -37,9 +61,9 @@ }, { "active": false, + "executeimmediate": false, "name" : "Egy archiválás", "template": "archive-ondemand.xml", - "executeimmediate": true, "cronexpression": "0 0 0/1 1/1 * ? *", "parameters": [ {"name": "sourcePath", "value": "/mnt/ISILON/ARCHIVE/ONE", "type": "java.lang.String"}, @@ -69,9 +93,9 @@ }, { "active": false, + "executeimmediate": false, "name" : "NEXIO visszarögzített anyagok másolása az ISILON/ARCHIVE mappába", "template": "copyforarchive-nexio-recordings.xml", - "executeimmediate": true, "cronexpression": "0 0 0/1 1/1 * ? *", "parameters": [ {"name": "nexioPort", "value": 2098, "type": "java.lang.Integer"}, @@ -103,9 +127,9 @@ }, { "active": false, + "executeimmediate": false, "name" : "TRAFFIC anyagok visszatöltése", "template": "retrieve-traffic-missing-materials.xml", - "executeimmediate": false, "cronexpression": "0 0 0/1 1/1 * ? *", "parameters": [ {"name": "dbUrl", "value": "jdbc:sqlserver://10.10.1.45\\sql16;databaseName=PA_Echo;", "type": "java.lang.String"}, @@ -118,9 +142,9 @@ }, { "active": false, + "executeimmediate": false, "name" : "MORPHEUS anyagok visszatöltése", "template": "retrieve-morpheus-missing-materials.xml", - "executeimmediate": true, "cronexpression": "0 0 0/1 1/1 * ? *", "parameters": [ {"name": "sourcePath", "value": "/mnt/MORPHEUS", "type": "java.lang.String"}, @@ -140,9 +164,9 @@ }, { "active": false, + "executeimmediate": false, "name" : "OCTOPUS adatok szinkronizálása", "template": "sync-octopus.xml", - "executeimmediate": true, "cronexpression": "0/20 * * * * ?", "parameters": [ {"name": "includeArchived", "value": false, "type": "java.lang.Boolean"} @@ -150,9 +174,9 @@ }, { "active": false, + "executeimmediate": false, "name" : "OCTOPUS adatok szinkronizálása archivált tükrökkel együtt", "template": "sync-octopus.xml", - "executeimmediate": true, "cronexpression": "0/30 * * * * ?", "parameters": [ {"name": "includeArchived", "value": true, "type": "java.lang.Boolean"} @@ -160,17 +184,17 @@ }, { "active": false, + "executeimmediate": false, "name" : "Párhuzamosított teszt folyamat", "template": "fake-concurrent.xml", - "executeimmediate": false, "cronexpression": "0/10 * * ? * *", "parameters": [ {"name": "itemID", "value": 1, "type": "java.lang.Long"} ] }, { "active": false, + "executeimmediate": false, "name" : "Teszt folyamat", "template": "fake-noparams.xml", - "executeimmediate": false, "cronexpression": "0 40 22 * * ?", "parameters": [ {"name": "itemID", "value": 1, "type": "java.lang.Long"} ] } diff --git a/server/user.jobengine.executors/config/scheduledjobs.json b/server/user.jobengine.executors/config/scheduledjobs.json index 9454a95d..f4e0d2ac 100644 --- a/server/user.jobengine.executors/config/scheduledjobs.json +++ b/server/user.jobengine.executors/config/scheduledjobs.json @@ -1,9 +1,9 @@ {"joblist":[ { "active": false, + "executeimmediate": false, "name" : "Híranyag statisztika importálása", "template": "import-statistics.xml", - "executeimmediate": false, "cronexpression": "0 0 0/1 1/1 * ? *", "parameters": [ {"name": "daysBeforeNow", "value": 1, "type": "java.lang.Integer"} @@ -11,9 +11,9 @@ }, { "active": false, + "executeimmediate": false, "name" : "MORPHEUS 'missing materials' importálása", "template": "import-morpheus-missing-materials.xml", - "executeimmediate": false, "cronexpression": "0 0 0/1 1/1 * ? *", "parameters": [ {"name": "csvFilePath", "value": "/mnt/MORPHEUS", "type": "java.lang.String"}, @@ -22,8 +22,8 @@ ] }, { - "active": true, - "executeimmediate": true, + "active": false, + "executeimmediate": false, "name" : "OCTOPUS adatok szinkronizálása", "template": "sync-octopus.xml", "cronexpression": "0/30 * * * * ?", @@ -32,7 +32,7 @@ ] }, { - "active": true, + "active": false, "executeimmediate": false, "name" : "Archiválás az ISILON/ARCHIVE mappából", "template": "archive-ondemand.xml", @@ -46,9 +46,9 @@ ] }, { - "active": true, + "active": false, "executeimmediate": false, - "name" : "NEXIO anyagok másolása az ISILON/ARCHIVE mappába", + "name" : "NEXIO bejátszók másolása az ISILON/ARCHIVE mappába", "template": "copyforarchive-nexio-materials.xml", "cronexpression": "0 0 10 * * ?", "parameters": [ @@ -65,6 +65,25 @@ }, { "active": false, + "executeimmediate": false, + "name" : "NEXIO visszarögzített anyagok másolása az ISILON/ARCHIVE mappába", + "template": "copyforarchive-nexio-recordings.xml", + "cronexpression": "0 0 0/1 1/1 * ? *", + "parameters": [ + {"name": "nexioPort", "value": 2098, "type": "java.lang.Integer"}, + {"name": "nexioUserName", "value": "administrator", "type": "java.lang.String"}, + {"name": "nexioPassword", "value": "system", "type": "java.lang.String"}, + {"name": "archiveFtp", "value": "ftp://10.10.1.100/ARCHIVE", "type": "java.lang.String"}, + {"name": "archiveUserName", "value": "mediacube", "type": "java.lang.String"}, + {"name": "archivePassword", "value": "Broadca5T", "type": "java.lang.String"}, + {"name": "filterAgencies", "value": "schedule-rec", "type": "java.lang.String"}, + {"name": "limit", "value": 10, "type": "java.lang.Integer"}, + {"name": "nexioKillDateDays", "value": 7, "type": "java.lang.Integer"}, + {"name": "nexioAgency", "value": "ARCHIVED", "type": "java.lang.String"} + ] + }, + { + "active": false, "executeimmediate": false, "name" : "Lejárt ISISLON/ARCHIVE anyagok törlése", "template": "delete-materials.xml", @@ -75,7 +94,7 @@ }, { "active": false, - "executeimmediate": true, + "executeimmediate": false, "name" : "Lejárt ISISLON/ARCHIVE_TEMP anyagok törlése", "template": "delete-materials.xml", "cronexpression": "0 0 0/1 1/1 * ? *", @@ -84,7 +103,7 @@ ] }, { - "active": true, + "active": false, "executeimmediate": false, "name" : "Lejárt NEXIO anyagok törlése", "template": "delete-nexio-materials.xml", @@ -100,7 +119,7 @@ }, { "active": false, - "executeimmediate": true, + "executeimmediate": false, "name" : "TRAFFIC anyagok visszatöltése", "template": "retrieve-traffic-missing-materials.xml", "cronexpression": "0 0 0/1 1/1 * ? *", @@ -115,7 +134,7 @@ }, { "active": false, - "executeimmediate": true, + "executeimmediate": false, "name" : "MORPHEUS anyagok visszatöltése", "template": "retrieve-morpheus-missing-materials.xml", "cronexpression": "0 0 0/1 1/1 * ? *", @@ -143,9 +162,9 @@ }, { "active": false, + "executeimmediate": false, "name" : "Teszt fork-párhuzamosított folyamat", "template": "fake-noparams.xml", - "executeimmediate": false, "cronexpression": "0 40 22 * * ?", "parameters": [ {"name": "itemID", "value": 1, "type": "java.lang.Long"} ] } diff --git a/server/user.jobengine.executors/jobtemplates/archive-recording.xml b/server/user.jobengine.executors/jobtemplates/archive-recording.xml new file mode 100644 index 00000000..4afd9bda --- /dev/null +++ b/server/user.jobengine.executors/jobtemplates/archive-recording.xml @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/server/user.jobengine.executors/jobtemplates/import-morpheus-missing-materials.xml b/server/user.jobengine.executors/jobtemplates/import-morpheus-missing-materials.xml index c339f067..9745eaf4 100644 --- a/server/user.jobengine.executors/jobtemplates/import-morpheus-missing-materials.xml +++ b/server/user.jobengine.executors/jobtemplates/import-morpheus-missing-materials.xml @@ -6,6 +6,9 @@ + + + diff --git a/server/user.jobengine.executors/jobtemplates/test-multiparam.xml b/server/user.jobengine.executors/jobtemplates/test-multiparam.xml new file mode 100644 index 00000000..bf45329b --- /dev/null +++ b/server/user.jobengine.executors/jobtemplates/test-multiparam.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/server/user.jobengine.executors/src/user/jobengine/server/steps/CopyForArchiveNEXIORecordingsStep.java b/server/user.jobengine.executors/src/user/jobengine/server/steps/CopyForArchiveNEXIORecordingsStep.java index dcd4ba61..98cc14ed 100644 --- a/server/user.jobengine.executors/src/user/jobengine/server/steps/CopyForArchiveNEXIORecordingsStep.java +++ b/server/user.jobengine.executors/src/user/jobengine/server/steps/CopyForArchiveNEXIORecordingsStep.java @@ -50,7 +50,6 @@ public class CopyForArchiveNEXIORecordingsStep extends JobStep { private static final String MXFEXT = ".MXF"; private static final String NEXIOCLIPS = "nexioclips"; private static final String LONGNAMEID = "longnameid"; - private static final String ID = "id"; private static final String EXTAGENCY = "extagency"; private static final String RECORDDATE = "recorddate"; private static final SimpleDateFormat startTimeformat = new SimpleDateFormat(STARTTIME_FORMAT); @@ -230,7 +229,7 @@ public class CopyForArchiveNEXIORecordingsStep extends JobStep { private RundownArchive processRundow(DBObject r, String clipName, long duration) throws Exception { BasicDBObject rundown = (BasicDBObject) r; - long rundownID = rundown.getLong(ID); + long rundownID = rundown.getLong(IOctopusAPI.ID); logger.info("Processing rundown {} {}", rundownID, rundown.getString(IOctopusAPI.NAME)); List stories = octopusAPI.getRundownFullStories(rundownID); diff --git a/server/user.jobengine.executors/src/user/jobengine/server/steps/MetadataTransformStep.java b/server/user.jobengine.executors/src/user/jobengine/server/steps/MetadataTransformStep.java index 80d2c91c..9b4631a7 100644 --- a/server/user.jobengine.executors/src/user/jobengine/server/steps/MetadataTransformStep.java +++ b/server/user.jobengine.executors/src/user/jobengine/server/steps/MetadataTransformStep.java @@ -4,15 +4,17 @@ import java.io.File; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.nio.file.attribute.FileAttribute; +import java.nio.file.attribute.PosixFilePermission; +import java.nio.file.attribute.PosixFilePermissions; import java.util.Date; +import java.util.Set; -import org.apache.commons.lang.StringUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import user.jobengine.db.Item; import user.jobengine.db.ItemManager; -import user.jobengine.db.ItemType; import user.jobengine.db.Media; import user.jobengine.server.IJobEngine; import user.jobengine.server.IJobRuntime; @@ -23,7 +25,7 @@ import user.jobengine.server.IJobRuntime; * @author robi */ public class MetadataTransformStep extends JobStep { - private static final String CONFLICT = "CONFLICT"; + private static final String CONFLICT = ".CONFLICT"; private static final Logger logger = LogManager.getLogger(); private static final String ITEM_MANAGER_IS_NULL = "ItemManager is null"; public static final String DEFAULT_MEDIATYPE = "Generic"; @@ -44,8 +46,24 @@ public class MetadataTransformStep extends JobStep { try { Path sourcePath = Paths.get(archiveItem.getMediaFile()); Path parent = sourcePath.getParent(); - EscortFiles.ensureUNCFolder(parent.toString(), CONFLICT); - Files.move(sourcePath, Paths.get(parent.toString(), sourceFileName + (new Date()).getTime())); + Path conflictPath = Paths.get(parent.toString(), CONFLICT); + File folder = conflictPath.toFile(); + if (!folder.exists() || !folder.isDirectory()) { + Set perms = PosixFilePermissions.fromString("rwxrwxrwx"); + FileAttribute> attr = PosixFilePermissions.asFileAttribute(perms); + try { + Files.createDirectories(conflictPath, attr); + } catch (Exception e) { + try { + Files.createDirectory(conflictPath); + } catch (Exception e1) { + logger.catching(e); + throw e; + } + } + } + + Files.move(sourcePath, Paths.get(conflictPath.toString(), sourceFileName + (new Date()).getTime())); } catch (Exception e1) { logger.catching(e1); logger.error(getMarker(), "Hiba az állomány {} mappába másolásakor. A rendszer üzenete: {}", CONFLICT, e1.getMessage()); @@ -60,17 +78,19 @@ public class MetadataTransformStep extends JobStep { mediaCubeItem = itemManager.createItem(DEFAULT_MEDIATYPE, archiveItem.getItemTitle(), archiveItem.getItemDescription(), archiveItem.getItemHouseId()); - String mediaType = archiveItem.getMediaType(); - if (StringUtils.isBlank(mediaType)) { - mediaCubeMedia = itemManager.createMedia(DEFAULT_MEDIATYPE, archiveItem.getMediaTitle(), archiveItem.getMediaDescription(), - archiveItem.getMediaHouseId()); - } else { - ItemType mediaItemType = itemManager.getItemType(mediaType); - if (mediaItemType == null) - itemManager.createItemType(mediaType, mediaType).add(); - mediaCubeMedia = itemManager.createMedia(mediaType, archiveItem.getMediaTitle(), archiveItem.getMediaDescription(), - archiveItem.getMediaHouseId()); - } + mediaCubeMedia = itemManager.createMedia(DEFAULT_MEDIATYPE, archiveItem.getMediaTitle(), archiveItem.getMediaDescription(), + archiveItem.getMediaHouseId()); + // String mediaType = archiveItem.getMediaType(); + // if (StringUtils.isBlank(mediaType)) { + // mediaCubeMedia = itemManager.createMedia(DEFAULT_MEDIATYPE, archiveItem.getMediaTitle(), archiveItem.getMediaDescription(), + // archiveItem.getMediaHouseId()); + // } else { + // ItemType mediaItemType = itemManager.getItemType(mediaType); + // if (mediaItemType == null) + // itemManager.createItemType(mediaType, mediaType).add(); + // mediaCubeMedia = itemManager.createMedia(mediaType, archiveItem.getMediaTitle(), archiveItem.getMediaDescription(), + // archiveItem.getMediaHouseId()); + // } mediaCubeMedia.setLength(archiveItem.getDuration()); mediaCubeItem.appendMedia(mediaCubeMedia); diff --git a/server/user.jobengine.executors/src/user/jobengine/server/steps/MultiParamStep.java b/server/user.jobengine.executors/src/user/jobengine/server/steps/MultiParamStep.java new file mode 100644 index 00000000..2d0f1850 --- /dev/null +++ b/server/user.jobengine.executors/src/user/jobengine/server/steps/MultiParamStep.java @@ -0,0 +1,15 @@ +package user.jobengine.server.steps; + +import user.jobengine.server.IJobEngine; +import user.jobengine.server.IJobRuntime; + +public class MultiParamStep extends JobStep { + + @StepEntry + public Object[] execute(long p1, String p2, IJobEngine jobEngine, IJobRuntime jobRuntime) throws Exception { + Object[] result = { p1 + 1, p2 + "x" }; + + return result; + } + +} diff --git a/server/user.jobengine.executors/src/user/jobengine/server/steps/OctopusDataMiner.java b/server/user.jobengine.executors/src/user/jobengine/server/steps/OctopusDataMiner.java index 0c6b2236..49aa17a2 100644 --- a/server/user.jobengine.executors/src/user/jobengine/server/steps/OctopusDataMiner.java +++ b/server/user.jobengine.executors/src/user/jobengine/server/steps/OctopusDataMiner.java @@ -39,6 +39,8 @@ import user.commons.remotestore.ProgressEvent; import user.jobengine.server.steps.MetadataTypeDetector.MetadataType; public class OctopusDataMiner { + private static final String MOS_ABSTRACT = ""; + private static final String MOS_ABSTRACT_END = ""; private static final Logger logger = LogManager.getLogger(); private static final String ARCHIVED = "archived"; private static final String FILTER = "filter"; @@ -60,7 +62,7 @@ public class OctopusDataMiner { private static final String ENTER = "Enter"; private static final String FINISHED = "Finished"; private static final String STARTING = "Starting"; - private static final String MOSOBJECT = "Bejátszó: "; + private static final String MOSLABEL = "MOS: "; private static final Object STORY = "Story"; private DB db; @@ -237,9 +239,14 @@ public class OctopusDataMiner { case IOctopusAPI.MOS: { BasicDBObject mosObject = NoSQLUtils.asDBObject(content, IOctopusAPI.OBJECT); if (mosObject != null && !mosObject.isEmpty()) { - if (mosObject.containsKey(IOctopusAPI.OBJ_ID)) { - String objID = mosObject.getString(IOctopusAPI.OBJ_ID); - scriptContent += String.format("%s %s%s", MOSOBJECT, objID, LINEFEED); + String xml = NoSQLUtils.asString(mosObject, IOctopusAPI.XML); + if (xml != null) { + int pos1 = xml.indexOf(MOS_ABSTRACT) + MOS_ABSTRACT.length(); + int pos2 = xml.indexOf(MOS_ABSTRACT_END); + + if (pos1 > -1 && pos2 > -1) { + scriptContent += String.format("%s %s%s", MOSLABEL, xml.substring(pos1, pos2), LINEFEED); + } } } break; @@ -324,6 +331,10 @@ public class OctopusDataMiner { StringBuilder sb = new StringBuilder(); for (BasicDBObject bodyItem : body) { + if (bodyItem.containsKey(IOctopusAPI.TYPE)) { + sb.append(bodyItem.getString(IOctopusAPI.TYPE)); + sb.append(LINEFEED); + } if (bodyItem.containsKey(IOctopusAPI.LABEL)) { sb.append(bodyItem.getString(IOctopusAPI.LABEL)); sb.append(LINEFEED); @@ -600,12 +611,14 @@ public class OctopusDataMiner { story.put(IOctopusAPI.REF_RUNDOWN, rundownRef); if (storyFolderRef != null) story.put(IOctopusAPI.REF_STORYFOLDER, storyFolderRef); + String scriptContent = extractScriptContent(story); story.put(IOctopusAPI.SCRIPT_CONTENT, scriptContent); - if (modifiedMOS == null || modifiedMOS.isEmpty()) { - if (story.containsKey(IOctopusAPI.MOS_OBJECTS)) - story.remove(IOctopusAPI.MOS_OBJECTS); - } else + story.remove(IOctopusAPI.SCRIPT); + + if (modifiedMOS == null || modifiedMOS.isEmpty()) + story.remove(IOctopusAPI.MOS_OBJECTS); + else story.put(IOctopusAPI.MOS_OBJECTS, modifiedMOS); String parentStoryId = extractCustomColumnValue(IOctopusAPI.PARENT_STORY_ID, story); @@ -614,6 +627,9 @@ public class OctopusDataMiner { story.append(IOctopusAPI.PARENT_STORY_ID, storyID); } else story.append(IOctopusAPI.PARENT_STORY_ID, parentStoryId); + + story.remove(IOctopusAPI.CUSTOM_COLUMNS); + logger.debug(SAVING_STORY_ID, storyID); setObjectID(currentStories, storyID, story); if (!disableWrite) diff --git a/server/user.jobengine.executors/src/user/jobengine/server/steps/RecordingsArchiveItemBuilderStep.java b/server/user.jobengine.executors/src/user/jobengine/server/steps/RecordingsArchiveItemBuilderStep.java new file mode 100644 index 00000000..599a73c9 --- /dev/null +++ b/server/user.jobengine.executors/src/user/jobengine/server/steps/RecordingsArchiveItemBuilderStep.java @@ -0,0 +1,281 @@ +package user.jobengine.server.steps; + +import java.io.File; +import java.io.IOException; +import java.nio.file.FileVisitResult; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.attribute.BasicFileAttributes; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.List; + +import org.apache.commons.lang.StringUtils; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.Marker; + +import com.ibm.nosql.json.api.BasicDBObject; +import com.ibm.nosql.json.api.DBObject; + +import user.commons.CalendarUtils; +import user.commons.nosql.NoSQLUtils; +import user.commons.octopus.IOctopusAPI; +import user.commons.octopus.OctopusAPI; +import user.jobengine.server.IJobEngine; +import user.jobengine.server.IJobRuntime; + +public class RecordingsArchiveItemBuilderStep extends JobStep { + private static final String RECORDING = "Visszarögzítés"; + private static final Logger logger = LogManager.getLogger(); + private static final String STATUSFOLDER = ".STATUS"; + private static final String LXFEXT = ".lxf"; + private static final String CATCHEDEXT = ".catched"; + private static final SimpleDateFormat startTimeformat = new SimpleDateFormat("HHmm"); + private static final SimpleDateFormat startDateformat = new SimpleDateFormat("yyMMdd"); + private static final String SCHEDULED_FORMAT = "yyyy.MM.dd HH:mm"; + + private Marker marker; + + private ArchiveItem createArchiveItem(Path mediaFilePath, Path catchedFilePath) { + ArchiveItem result = null; + try { + + Date recordDate = startDateformat.parse(mediaFilePath.getParent().toFile().getName()); + String clipName = mediaFilePath.toFile().getName(); + Date scheduledStart = getScheduledStart(clipName, recordDate); + IOctopusAPI octopusAPI = new OctopusAPI(); + DBObject rundown = octopusAPI.getRundown(scheduledStart); + if (rundown == null) { + logger.error(marker, "A '{}' anyaghoz nem található tükör '{}' kezdéssel, ezért nem archiválható.", clipName, scheduledStart); + return null; + } + //DB db = NoSQLUtils.getNoSQLDB(); + + // + // BasicDBObject dbObject = (BasicDBObject) JSONUtil.jsonToDbObject(new String(readAllBytes)); + // if (dbObject == null) + // throw new NullPointerException("Can not parse JSON file: " + jsonFilePath); + result = processRundow(octopusAPI, rundown); + if (result == null) + return null; + + result.setMediaTitle(clipName); + result.setMediaType(RECORDING); + result.setMediaFile(mediaFilePath.toString()); + result.setCatchedFile(catchedFilePath.toString()); + // result.setDuration(NoSQLUtils.asLong(dbObject, DURATION)); + } catch (Exception e) { + logger.catching(e); + logger.error(getJobRuntime().getMarker(), "A metaadat nem elérhető. A rendszer üzenete: {}", e.getMessage()); + return null; + } + + return result; + } + + private void createCatchedFile(Path catchedFilePath) throws Exception { + try { + EscortFiles.ensureUNCFolder(catchedFilePath.getParent()); + Files.createFile(catchedFilePath); + } catch (Exception e) { + logger.catching(e); + logger.error(marker, "A '{}' jelzőfájl nem hozható létre. A rendszer üzenete: {}", catchedFilePath, e.getMessage()); + throw e; + } + } + + @StepEntry + public Object[] execute(String sourcePath, IJobEngine jobEngine, IJobRuntime jobRuntime) { + final ArchiveItem[] archiveItems = { null }; + marker = getJobRuntime().getMarker(); + try { + Files.walkFileTree(Paths.get(sourcePath), new SimpleFileVisitor() { + + @Override + public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { + FileVisitResult result = FileVisitResult.SKIP_SUBTREE; + + if (dir.equals(Paths.get(sourcePath)) || "HIRADO".equals(dir.toFile().getName().toUpperCase()) + || "NAPIAKT".equals(dir.toFile().getName().toUpperCase())) + result = FileVisitResult.CONTINUE; + else { + if ("HIRADO".equals(dir.getParent().toFile().getName().toUpperCase()) + || "NAPIAKT".equals(dir.getParent().toFile().getName().toUpperCase())) { + try { + startDateformat.parse(dir.toFile().getName()); + result = FileVisitResult.CONTINUE; + } catch (ParseException e) { + } + } + } + + if (result == FileVisitResult.CONTINUE) + logger.info(dir); + return result; + } + + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + FileVisitResult result = FileVisitResult.TERMINATE; + ArchiveItem item = null; + try { + item = processPathItem(file); + } catch (Exception e) { + logger.catching(e); + logger.error(marker, "Az '{}' állomány feldolgozása sikertelen. A rendszer hibaüzenete: {}", file, e.getMessage()); + } + if (item == null) { + result = FileVisitResult.CONTINUE; + } else { + logger.info(file); + archiveItems[0] = item; + } + return result; + } + + }); + + } catch (Exception e) { + logger.catching(e); + logger.error(marker, "Az '{}' mappa elérése sikertelen. A rendszer hibaüzenete: {}", sourcePath, e.getMessage()); + } finally { + } + ArchiveItem archiveItem = archiveItems[0]; + String targetFileName = null; + + if (archiveItem == null || archiveItem.getMediaFile() == null) + logger.warn(marker, "Az archiváló folyamat nem talált új anyagot."); + else { + String mediaFile = archiveItem.getMediaFile(); + String name = new File(mediaFile).getName(); + int extPos = name.toLowerCase().lastIndexOf(LXFEXT); + targetFileName = String.format("20%s-%s", Paths.get(mediaFile).getParent().getFileName(), name.substring(0, extPos)); + logger.info(marker, "Az archiváló folyamat az '{}' anyagot archiválja.", mediaFile); + } + + return new Object[] { archiveItem, targetFileName }; + } + + private Date getScheduledStart(String clipName, Date recordDate) { + + if (StringUtils.isBlank(clipName)) { + logger.warn(marker, "A fájlnak nincs neve, ezért nem archiválható."); + return null; + } + if (recordDate == null) { + logger.warn(marker, "Az '{}' fájl rögzítésének ideje nem meghatározható, ezért nem archiválható.", clipName); + return null; + } + + Date timePart = null; + try { + String clipNameTime = clipName.split("_")[0]; + timePart = startTimeformat.parse(clipNameTime); + } catch (ParseException e) { + logger.warn(marker, "A '{}' fájl neve nem időbélyeggel kezdődik, ezért nem archiválható.", clipName); + return null; + } + return CalendarUtils.createCalendar(CalendarUtils.createCalendar(recordDate), timePart).getTime(); + } + + private ArchiveItem processPathItem(Path mediaFilePath) throws Exception { + File mediaFile = mediaFilePath.toFile(); + + Path dotStorePath = Paths.get(mediaFilePath.getParent().toString(), STATUSFOLDER); + Path catchedFilePath = Paths.get(dotStorePath.toString(), mediaFile.getName() + CATCHEDEXT); + File catchedFile = catchedFilePath.toFile(); + if (catchedFile.exists()) { + //logger.info("'{}' file is already catched", mediaFilePath); + return null; + } + createCatchedFile(catchedFilePath); + if (!catchedFile.exists()) { + logger.warn("'{}' catchfile not exists.", catchedFilePath); + return null; + } + + if (mediaFile.isDirectory() || !mediaFile.getName().toLowerCase().endsWith(LXFEXT.toLowerCase()) + || mediaFilePath.getParent().toFile().getName().length() != 6) { + logger.info("Skipping '{}'", mediaFilePath); + return null; + } + + ArchiveItem archiveItem = createArchiveItem(mediaFilePath, catchedFilePath); + + if (archiveItem == null) { + logger.warn("'{}' has no metadata specified.", mediaFilePath); + return null; + } + + if (StringUtils.isBlank(archiveItem.getItemHouseId())) { + logger.warn("'{}' has no Item HouseID specified in metadata.", mediaFilePath); + return null; + } + + if (StringUtils.isBlank(archiveItem.getItemTitle())) { + logger.warn("'{}' has no Item Title specified in metadata.", mediaFilePath); + return null; + } + + if (StringUtils.isBlank(archiveItem.getMediaHouseId())) { + logger.warn("'{}' has no Media HouseID specified in metadata.", mediaFilePath); + return null; + } + + if (StringUtils.isBlank(archiveItem.getMediaTitle())) { + logger.warn("'{}' has no Media Title specified in metadata.", mediaFilePath); + return null; + } + return archiveItem; + } + + private ArchiveItem processRundow(IOctopusAPI octopusAPI, DBObject r) throws Exception { + BasicDBObject rundown = (BasicDBObject) r; + long rundownID = rundown.getLong(IOctopusAPI.ID); + logger.info("Processing rundown {} {}", rundownID, rundown.getString(IOctopusAPI.NAME)); + + List stories = octopusAPI.getRundownFullStories(rundownID); + if (stories == null) + return null; + + String name = NoSQLUtils.asString(NoSQLUtils.asDBObject(rundown, IOctopusAPI.RUNDOWN_TYPE), IOctopusAPI.NAME); + if (StringUtils.isBlank(name)) + return null; + String channel = NoSQLUtils.asString(NoSQLUtils.asDBObject(rundown, IOctopusAPI.CHANNEL), IOctopusAPI.NAME); + Date scheduledStart = rundown.getDate(IOctopusAPI.SCHEDULED_START); + if (scheduledStart == null) + return null; + + ArchiveItem result = new ArchiveItem(); + result.setItemHouseId(String.valueOf(rundownID)); + String start = CalendarUtils.toString(CalendarUtils.createCalendar(scheduledStart), SCHEDULED_FORMAT); + result.setItemTitle(String.format("%s %s %s", start, name, channel)); + + StringBuilder sb = new StringBuilder(); + for (DBObject s : stories) { + BasicDBObject story = (BasicDBObject) s; + + sb.append("*** "); + sb.append(story.getString(IOctopusAPI.PARENT_STORY_ID)); + sb.append(" [" + story.getString(IOctopusAPI.FORMAT) + "] "); + sb.append(story.getString(IOctopusAPI.NAME)); + sb.append(" ***"); + sb.append("\r\n"); + String content = story.getString(IOctopusAPI.SCRIPT_CONTENT); + if (content != null) { + content = content.replace("\r\n\r\n\r\n\r\n", "\r\n"); + content = content.replace("\r\n\r\n\r\n", "\r\n"); + content = content.replace("\r\n\r\n", "\r\n"); + sb.append(content); + sb.append("\r\n"); + } + } + result.setMediaHouseId(result.getItemHouseId()); + result.setMediaDescription(sb.toString()); + return result; + } +} diff --git a/server/user.jobengine.executors/src/user/jobengine/server/steps/UploadRecordingToNexioStep.java b/server/user.jobengine.executors/src/user/jobengine/server/steps/UploadRecordingToNexioStep.java new file mode 100644 index 00000000..d5e3b790 --- /dev/null +++ b/server/user.jobengine.executors/src/user/jobengine/server/steps/UploadRecordingToNexioStep.java @@ -0,0 +1,141 @@ +package user.jobengine.server.steps; + +import java.io.File; +import java.nio.file.Paths; + +import org.apache.commons.lang.StringUtils; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.Marker; +import org.apache.logging.log4j.message.Message; +import org.apache.logging.log4j.message.ParameterizedMessage; + +import com.ibm.nosql.json.api.DB; + +import user.commons.RemoteFile; +import user.commons.StoreUri; +import user.commons.nosql.NoSQLUtils; +import user.commons.octopus.OctopusAPI; +import user.commons.remotestore.IProgressEventListener; +import user.commons.remotestore.IStatusEventListener; +import user.commons.remotestore.ProgressEvent; +import user.commons.remotestore.RemoteStoreProtocol; +import user.commons.remotestore.StatusEvent; +import user.jobengine.db.IItemManager; +import user.jobengine.server.IJobEngine; +import user.jobengine.server.IJobRuntime; + +public class UploadRecordingToNexioStep extends JobStep { + private static final Logger logger = LogManager.getLogger(); + + private OctopusAPI octopusAPI; + private IItemManager manager; + private DB db; + private StoreUri sourceUri; + private StoreUri targetUri; + private Marker marker; + + private int check(int value, String name) { + if (value == 0) { + logger.error(marker, "A folyamat '{}' bemeneti paramétere 0.", name); + throw new NullPointerException(String.format("System is not configured properly, missing '%s' input parameter.", name)); + } + return value; + } + + private String check(String value, String name) { + if (StringUtils.isBlank(value)) { + logger.error(marker, "A folyamat '{}' bemeneti paramétere üres.", name); + throw new NullPointerException(String.format("System is not configured properly, missing '%s' input parameter.", name)); + } + return value; + } + + @StepEntry + public Object[] execute(ArchiveItem archiveItem, String targetFileName, int nexioPort, String nexioUserName, String nexioPassword, IJobEngine jobEngine, + IJobRuntime jobRuntime) throws Exception { + marker = jobRuntime.getMarker(); + manager = jobEngine.getItemManager(); + setAndCheck(archiveItem, targetFileName, nexioPort, nexioUserName, nexioPassword); + String sourceFileName = new File(archiveItem.getMediaFile()).getName(); + try { + sourceUri.addProgressListener(new IProgressEventListener() { + @Override + public void progressChanged(ProgressEvent evt) { + setProgress(evt.getProgress()); + } + }); + sourceUri.addStatusListener(new IStatusEventListener() { + @Override + public void statusChanged(StatusEvent evt) { + evt.setCancel(!canContinue()); + } + }); + + RemoteFile targetFile = null; + try { + targetFile = targetUri.getRemoteFile(targetFileName); + } catch (Exception e) { + logger.catching(e); + } + if (targetFile != null) + throw new Exception("Exists!"); + + RemoteFile remoteFile = sourceUri.transferFrom(targetUri, sourceFileName, targetFileName); + + logger.info(marker, "Az '{}' állomány feltöltése sikeres volt '{}' néven.", sourceFileName, targetFileName); + } catch (Exception e) { + logger.catching(e); + Message m = new ParameterizedMessage("Az '{}' állomány feltöltése sikertelen. A rendszer hibaüzenete: {}", sourceFileName, e.getMessage()); + logger.error(marker, m); + throw new Exception(m.getFormattedMessage()); + } + return null; + } + + private void setAndCheck(ArchiveItem archiveItem, String targetFileName, int nexioPort, String nexioUserName, String nexioPassword) throws Exception { + db = NoSQLUtils.getNoSQLDB(); + if (db == null) { + logger.error(marker, "Az NoSQL adatkezelő réteg nem elérhető."); + throw new NullPointerException("Internal error, missing NoSQL DB reference."); + } + + if (archiveItem == null) { + logger.error(marker, "A folyamat 'archiveItem' bemeneti paramétere üres."); + throw new NullPointerException("Internal error, missing 'archiveItem'."); + } + + if (archiveItem.getMediaFile() == null) { + logger.error(marker, "A folyamat 'archiveItem.mediaFile' paramétere üres."); + throw new NullPointerException("Internal error, missing 'archiveItem.mediaFile'."); + } + + check(targetFileName, "targetFileName"); + + String nexioHost = System.getProperty("nexio.host"); + if (StringUtils.isBlank(nexioHost)) { + logger.error(marker, "A 'nexio.host' rendszer paraméter nem található."); + throw new NullPointerException("System is not configured properly, 'jobengine.selenio.address' startup parameter missing."); + } + check(nexioPort, "nexioPort"); + check(nexioUserName, "nexioUserName"); + check(nexioPassword, "nexioPassword"); + + targetUri = manager.createStoreUri(RemoteStoreProtocol.FTP, nexioHost); + targetUri.setPortNumber(nexioPort); + targetUri.setUserName(nexioUserName); + targetUri.setPassword(nexioPassword); + if (targetUri == null) { + logger.error(marker, "A forrás nem elérhető."); + throw new NullPointerException("Internal error, missing 'sourceUri'."); + } + + sourceUri = manager.createStoreUri(RemoteStoreProtocol.LOCAL, Paths.get(archiveItem.getMediaFile()).getParent().toString()); + if (sourceUri == null) { + logger.error(marker, "A cél nem elérhető."); + throw new NullPointerException("Internal error, missing 'targetUri'."); + } + + } + +} diff --git a/server/user.jobengine.osgi.commons/src/user/commons/StoreUri.java b/server/user.jobengine.osgi.commons/src/user/commons/StoreUri.java index 3b0daa20..2f050895 100644 --- a/server/user.jobengine.osgi.commons/src/user/commons/StoreUri.java +++ b/server/user.jobengine.osgi.commons/src/user/commons/StoreUri.java @@ -88,13 +88,15 @@ public class StoreUri extends EntityBase implements Serializable { String targetFileName = targetName; if (StringUtils.isEmpty(targetFileName)) targetFileName = source.getName(); - else { - String name = source.getName(); - int sep1 = name.lastIndexOf("."); - int sep2 = targetFileName.lastIndexOf("."); - if (sep2 == -1) - targetFileName = targetFileName + name.substring(sep1); - } + + // Nexio feltöltésnél nem jó ha automatikusan másoljuk a kiterjesztést + // else { + // String name = source.getName(); + // int sep1 = name.lastIndexOf("."); + // int sep2 = targetFileName.lastIndexOf("."); + // if (sep2 == -1) + // targetFileName = targetFileName + name.substring(sep1); + // } result.setName(targetFileName); result.setSize(source.getSize()); OutputStream outputStream = outputLister.getOutputStream(result); diff --git a/server/user.jobengine.osgi.commons/src/user/commons/octopus/IOctopusAPI.java b/server/user.jobengine.osgi.commons/src/user/commons/octopus/IOctopusAPI.java index e8e7f38f..5b26264a 100644 --- a/server/user.jobengine.osgi.commons/src/user/commons/octopus/IOctopusAPI.java +++ b/server/user.jobengine.osgi.commons/src/user/commons/octopus/IOctopusAPI.java @@ -25,6 +25,7 @@ public interface IOctopusAPI { static final String LABEL = "label"; static final String CLIP = "clip"; static final String OBJ_ID = "objId"; + static final String XML = "xml"; static final String OBJECT = "object"; static final String MOS = "mos"; static final String TEXT = "text"; diff --git a/server/user.jobengine.osgi.commons/src/user/commons/octopus/OctopusAPI.java b/server/user.jobengine.osgi.commons/src/user/commons/octopus/OctopusAPI.java index b1d208f0..59ad1ab8 100644 --- a/server/user.jobengine.osgi.commons/src/user/commons/octopus/OctopusAPI.java +++ b/server/user.jobengine.osgi.commons/src/user/commons/octopus/OctopusAPI.java @@ -3,6 +3,7 @@ package user.commons.octopus; import java.sql.SQLException; import java.util.ArrayList; import java.util.Calendar; +import java.util.Collections; import java.util.Date; import java.util.List; @@ -87,8 +88,23 @@ public class OctopusAPI implements IOctopusAPI { DBCollection collection = db.getCollection(STORY_COLLECTION); DBCursor find = collection.find(new BasicDBObject(REF_RUNDOWN, new BasicDBObject($ELEMMATCH, new BasicDBObject(ID, id)))) .sort(new BasicDBObject("name", 1)); - if (find.hasNext()) + if (find.hasNext()) { result = find.toArray(); + result.forEach(o -> { + BasicDBObject dbo = (BasicDBObject) o; + NoSQLUtils.asList(dbo, REF_RUNDOWN).forEach(io -> { + if (io.getLong(IOctopusAPI.ID) == id) + o.put(IOctopusAPI.POSITION, io.getLong(IOctopusAPI.POSITION)); + }); + + }); + Collections.sort(result, (o1, o2) -> { + long long1 = ((BasicDBObject) o1).getLong(IOctopusAPI.POSITION); + long long2 = ((BasicDBObject) o2).getLong(IOctopusAPI.POSITION); + return (int) (long1 - long2); + }); + } + return result; } diff --git a/server/user.jobengine.osgi.commons/test/user/common/octopus/test/OctopusDataMinerTest.java b/server/user.jobengine.osgi.commons/test/user/common/octopus/test/OctopusDataMinerTest.java index f7238181..0b5670ea 100644 --- a/server/user.jobengine.osgi.commons/test/user/common/octopus/test/OctopusDataMinerTest.java +++ b/server/user.jobengine.osgi.commons/test/user/common/octopus/test/OctopusDataMinerTest.java @@ -216,6 +216,79 @@ public class OctopusDataMinerTest { System.out.println(count); } + @Test + public void test5() throws Exception { + List restorables = Files.readAllLines(Paths.get("c:\\Temp\\restorables3.txt")); + List dates = new ArrayList<>(); + // String last = null; + for (String line : restorables) { + if (StringUtils.isBlank(line)) + continue; + + int pos = line.indexOf('\t'); + String date = line.substring(2, line.indexOf(' ')).replace(".", ""); + String name = line.substring(pos + 1); + dates.add(date + "-" + name); + // if (name.equals(last)) { + // } else { + // last = name; + // if (dates.size() > 1) + // dates.remove(dates.get(dates.size() - 1)); + // } + } + System.out.println(dates.size()); + + int count = 0; + List saved = Files.readAllLines(Paths.get("c:\\Temp\\list.txt")); + for (String line : saved) { + if (StringUtils.isBlank(line)) + continue; + + int pos1 = line.lastIndexOf('\\'); + int pos2 = line.lastIndexOf('\\', pos1 - 1); + String date = line.substring(pos2 + 1, pos1); + String name = line.substring(pos1 + 1).replace(".lxf", ".MXF"); + + String o = date + "-" + name; + if (dates.contains(o)) { + System.out.println(o); + count++; + } + + } + System.out.println(count); + + // Date limit = new Date(2018 - 1900, 1, 19, 6, 0, 0); + // Set keys = dates.keySet(); + // for (String name : keys) { + // + // boolean haslower = false; + // boolean hasupper = false; + // //System.out.println("processing " + name); + // + // for (Date date : dates.get(name)) { + // + // if (date.before(limit)) + // haslower = true; + // if (date.after(limit)) + // hasupper = true; + // //System.out.println("checked " + df.format(date) + " " + haslower + " " + hasupper); + // } + // + // if (haslower && hasupper) { + // System.out.println(name); + // count++; + // } + // } + // System.out.println(count); + } + + @Test + public void test6() throws Exception { + BasicDBObject x = new BasicDBObject(); + x.remove("y"); + } + @Test public void testClient() { ResteasyWebTarget webTarget = new ResteasyClientBuilder().build().target("http://10.10.1.28/services/rest/octopus/"); diff --git a/server/user.jobengine.osgi.db/sql/2-CreateStructure.db2 b/server/user.jobengine.osgi.db/sql/2-CreateStructure.db2 index 32c9ca3c..fc42b36d 100644 --- a/server/user.jobengine.osgi.db/sql/2-CreateStructure.db2 +++ b/server/user.jobengine.osgi.db/sql/2-CreateStructure.db2 @@ -250,7 +250,7 @@ CREATE TABLE MEDIAFILE MEDIAID BIGINT NOT NULL, STOREID BIGINT NOT NULL, FILETYPEID BIGINT NOT NULL, - RELATIVEPATH VARCHAR(255) NOT NULL, + RELATIVEPATH VARCHAR(1000) NOT NULL, FILESTRUCTINFO VARCHAR(255), CONSTRAINT FK_MEDIAFILE_MEDIAID FOREIGN KEY (MEDIAID) REFERENCES MEDIA (ID), CONSTRAINT FK_MEDIAFILE_STOREID FOREIGN KEY (STOREID) REFERENCES STORE (ID), diff --git a/server/user.jobengine.osgi.db/sql/stat-details.sql b/server/user.jobengine.osgi.db/sql/stat-details.sql index 46382e52..786418a8 100644 --- a/server/user.jobengine.osgi.db/sql/stat-details.sql +++ b/server/user.jobengine.osgi.db/sql/stat-details.sql @@ -1,4 +1,206 @@ -select * from mediafile where houseid = 'M009781A-0.MXF' +2.19 6:00 + +0700_hirado_PGM_-_H.MXF + +select itemtitle, mediafilehouseid, archived from vw_items where mediafilehouseid = '2200_hirado_PGM_-_SZO1.MXF' order by archived +select itemtitle, mediafilehouseid, archived from vw_items where mediafilehouseid in ( +'1900_hirado_napiakt_PGM_-_CS.MXF', +'1900_hirado_napiakt_PGM_-_H.MXF', +'0700_hirado_PGM_-_CS.MXF', +'0700_hirado_PGM_-_H.MXF', +'0700_hirado_PGM_-_P1.MXF', +'0700_hirado_PGM_-_SZO.MXF', +'0700_hirado_PGM_-_V.MXF', +'0800_hirado_PGM_-_CS.MXF', +'0800_hirado_PGM_-_H.MXF', +'0800_hirado_PGM_-_P1.MXF', +'0800_hirado_PGM_-_SZO.MXF', +'0800_hirado_PGM_-_V.MXF', +'0900_hirado_PGM_-_CS.MXF', +'0900_hirado_PGM_-_H.MXF', +'0900_hirado_PGM_-_P1.MXF', +'0900_hirado_PGM_-_SZO.MXF', +'0900_hirado_PGM_-_V.MXF', +'1000_hirado_PGM_-_CS.MXF', +'1000_hirado_PGM_-_H.MXF', +'1000_hirado_PGM_-_P1.MXF', +'1000_hirado_PGM_-_SZO.MXF', +'1000_hirado_PGM_-_V.MXF', +'1100_hirado_PGM_-_CS.MXF', +'1100_hirado_PGM_-_H.MXF', +'1100_hirado_PGM_-_P1.MXF', +'1100_hirado_PGM_-_SZO.MXF', +'1100_hirado_PGM_-_V.MXF', +'1200_hirado_PGM_-_CS.MXF', +'1200_hirado_PGM_-_H.MXF', +'1200_hirado_PGM_-_P1.MXF', +'1200_hirado_PGM_-_SZE.MXF', +'1200_hirado_PGM_-_SZO.MXF', +'1200_hirado_PGM_-_V.MXF', +'1300_hirado_PGM_-_CS.MXF', +'1300_hirado_PGM_-_H.MXF', +'1300_hirado_PGM_-_P1.MXF', +'1300_hirado_PGM_-_SZE.MXF', +'1300_hirado_PGM_-_SZO.MXF', +'1300_hirado_PGM_-_V.MXF', +'1400_hirado_PGM_-_CS.MXF', +'1400_hirado_PGM_-_H.MXF', +'1400_hirado_PGM_-_P1.MXF', +'1400_hirado_PGM_-_SZE.MXF', +'1400_hirado_PGM_-_SZO.MXF', +'1400_hirado_PGM_-_V.MXF', +'1500_hirado_PGM_-_CS.MXF', +'1500_hirado_PGM_-_H.MXF', +'1500_hirado_PGM_-_SZE.MXF', +'1500_hirado_PGM_-_SZO.MXF', +'1600_hirado_PGM_-_CS.MXF', +'1600_hirado_PGM_-_H.MXF', +'1600_hirado_PGM_-_P1.MXF', +'1600_hirado_PGM_-_SZE.MXF', +'1600_hirado_PGM_-_SZO.MXF', +'1700_hirado_PGM_-_CS.MXF', +'1700_hirado_PGM_-_H.MXF', +'1700_hirado_PGM_-_P.MXF', +'1700_hirado_PGM_-_SZE.MXF', +'1700_hirado_PGM_-_SZO.MXF', +'1700_hirado_PGM_-_V.MXF', +'1800_hirado_PGM_-_CS.MXF', +'1800_hirado_PGM_-_H.MXF', +'1800_hirado_PGM_-_P1.MXF', +'1800_hirado_PGM_-_SZE.MXF', +'1800_hirado_PGM_-_SZO.MXF', +'1800_hirado_PGM_-_V.MXF', +'1900_hirado_napiakt_PGM_-_K.MXF', +'1900_hirado_napiakt_PGM_-_P.MXF', +'1900_hirado_napiakt_PGM_-_SZE.MXF', +'2000_hirado_PGM_-_CS.MXF', +'2000_hirado_PGM_-_H.MXF', +'2000_hirado_PGM_-_P.MXF', +'2000_hirado_PGM_-_SZE.MXF', +'2000_hirado_PGM_-_SZO.MXF', +'2000_hirado_PGM_-_V.MXF', +'2200_hirado_PGM_-_CS.MXF', +'2200_hirado_PGM_-_H.MXF', +'2200_hirado_PGM_-_P.MXF', +'2200_hirado_PGM_-_SZE.MXF', +'2200_hirado_PGM_-_SZO.MXF', +'2200_hirado_PGM_-_V.MXF', +'0700_hirado_PGM_-_CS2.MXF', +'0700_hirado_PGM_-_H1.MXF', +'0700_hirado_PGM_-_K.MXF', +'0700_hirado_PGM_-_K1.MXF', +'0700_hirado_PGM_-_P.MXF', +'0700_hirado_PGM_-_SZE.MXF', +'0700_hirado_PGM_-_SZE1.MXF', +'0700_hirado_PGM_-_SZO1.MXF', +'0700_hirado_PGM_-_V1.MXF', +'0800_hirado_PGM_-_H1.MXF', +'0800_hirado_PGM_-_K.MXF', +'0800_hirado_PGM_-_K1.MXF', +'0800_hirado_PGM_-_P.MXF', +'0800_hirado_PGM_-_SZE.MXF', +'0800_hirado_PGM_-_SZE1.MXF', +'0800_hirado_PGM_-_SZO1.MXF', +'0800_hirado_PGM_-_V1.MXF', +'0900_hirado_PGM_-_H1.MXF', +'0900_hirado_PGM_-_K.MXF', +'0900_hirado_PGM_-_K1.MXF', +'0900_hirado_PGM_-_P.MXF', +'0900_hirado_PGM_-_SZE.MXF', +'0900_hirado_PGM_-_SZE1.MXF', +'0900_hirado_PGM_-_SZO1.MXF', +'0900_hirado_PGM_-_V1.MXF', +'1000_hirado_PGM_-_H1.MXF', +'1000_hirado_PGM_-_K.MXF', +'1000_hirado_PGM_-_K1.MXF', +'1000_hirado_PGM_-_P.MXF', +'1000_hirado_PGM_-_SZE.MXF', +'1000_hirado_PGM_-_SZE1.MXF', +'1000_hirado_PGM_-_SZO1.MXF', +'1000_hirado_PGM_-_V1.MXF', +'1100_hirado_PGM_-_H1.MXF', +'1100_hirado_PGM_-_K.MXF', +'1100_hirado_PGM_-_K1.MXF', +'1100_hirado_PGM_-_P.MXF', +'1100_hirado_PGM_-_SZE.MXF', +'1100_hirado_PGM_-_SZE1.MXF', +'1100_hirado_PGM_-_SZO1.MXF', +'1100_hirado_PGM_-_V1.MXF', +'1200_hirado_PGM_-_CS1.MXF', +'1200_hirado_PGM_-_H1.MXF', +'1200_hirado_PGM_-_K.MXF', +'1200_hirado_PGM_-_K1.MXF', +'1200_hirado_PGM_-_P.MXF', +'1200_hirado_PGM_-_SZO1.MXF', +'1200_hirado_PGM_-_V1.MXF', +'1300_hirado_PGM_-_CS1.MXF', +'1300_hirado_PGM_-_H1.MXF', +'1300_hirado_PGM_-_K.MXF', +'1300_hirado_PGM_-_K1.MXF', +'1300_hirado_PGM_-_P.MXF', +'1300_hirado_PGM_-_SZO1.MXF', +'1300_hirado_PGM_-_V1.MXF', +'1400_hirado_PGM_-_CS1.MXF', +'1400_hirado_PGM_-_H1.MXF', +'1400_hirado_PGM_-_K.MXF', +'1400_hirado_PGM_-_K1.MXF', +'1400_hirado_PGM_-_P.MXF', +'1400_hirado_PGM_-_SZO1.MXF', +'1400_hirado_PGM_-_V1.MXF', +'1500_hirado_PGM_-_H1.MXF', +'1500_hirado_PGM_-_K.MXF', +'1500_hirado_PGM_-_K1.MXF', +'1500_hirado_PGM_-_P.MXF', +'1500_hirado_PGM_-_P1.MXF', +'1500_hirado_PGM_-_SZO1.MXF', +'1500_hirado_PGM_-_V.MXF', +'1500_hirado_PGM_-_V1.MXF', +'1600_hirado_PGM_-_H1.MXF', +'1600_hirado_PGM_-_K.MXF', +'1600_hirado_PGM_-_K1.MXF', +'1600_hirado_PGM_-_P.MXF', +'1600_hirado_PGM_-_SZO1.MXF', +'1600_hirado_PGM_-_V.MXF', +'1600_hirado_PGM_-_V1.MXF', +'1700_hirado_PGM_-_CS1.MXF', +'1700_hirado_PGM_-_H1.MXF', +'1700_hirado_PGM_-_K.MXF', +'1700_hirado_PGM_-_K1.MXF', +'1700_hirado_PGM_-_P1.MXF', +'1700_hirado_PGM_-_SZO1.MXF', +'1700_hirado_PGM_-_V1.MXF', +'1800_hirado_PGM_-_CS1.MXF', +'1800_hirado_PGM_-_H1.MXF', +'1800_hirado_PGM_-_K.MXF', +'1800_hirado_PGM_-_K1.MXF', +'1800_hirado_PGM_-_P.MXF', +'1800_hirado_PGM_-_SZO1.MXF', +'1800_hirado_PGM_-_V1.MXF', +'1900_hirado_napiakt_PGM_-_CS1.MXF', +'1900_hirado_napiakt_PGM_-_H1.MXF', +'1900_hirado_napiakt_PGM_-_K1.MXF', +'1900_hirado_napiakt_PGM_-_P1.MXF', +'1900_hirado_napiakt_PGM_-_SZE1.MXF', +'1900_hirado_PGM_-_V.MXF', +'2000_hirado_PGM_-_CS1.MXF', +'2000_hirado_PGM_-_H1.MXF', +'2000_hirado_PGM_-_K.MXF', +'2000_hirado_PGM_-_K1.MXF', +'2000_hirado_PGM_-_P1.MXF', +'2000_hirado_PGM_-_SZO1.MXF', +'2000_hirado_PGM_-_V1.MXF', +'2100_hirek_PGM_-_4.MXF', +'2100_hirek_PGM_-_5.MXF', +'2100_hirek_PGM_-_6.MXF', +'2200_hirado_PGM_-_CS1.MXF', +'2200_hirado_PGM_-_H1.MXF', +'2200_hirado_PGM_-_K.MXF', +'2200_hirado_PGM_-_K1.MXF', +'2200_hirado_PGM_-_P1.MXF', +'2200_hirado_PGM_-_SZO1.MXF', +'2200_hirado_PGM_-_V1.MXF' +) order by mediafilehouseid, archived + select *, replace(mediafilehouseid, concat('-',itemhouseid), '') from vw_items where itemtitle like '%Echo TV%' and itemtitle like '2%' and mediatitle not like '%CLN%' and mediatitle not like '%PGM%' select count(distinct(replace(mediafilehouseid, concat('-',itemhouseid), ''))) from vw_items where itemtitle like '%Echo TV%' and itemtitle like '2%' and mediatitle not like '%CLN%' and mediatitle not like '%PGM%' @@ -9,11 +211,18 @@ select count(distinct(mediafilehouseid)) from vw_items where itemtitle like 'Ech select * from vw_items where itemtitle like '%Echo TV%' and itemtitle like '2%' and mediatitle like '%PGM%' select count(distinct(mediafilehouseid)) from vw_items where itemtitle like '%Echo TV%' and itemtitle like '2%' and mediatitle like '%PGM%' +select concat('''', concat(mediatitle,'.MXF'',')), count(*) as c from vw_items where itemtitle like '%Echo TV%' and itemtitle like '2%' and mediatitle like '%PGM%' group by mediatitle order by c desc + +select mediafilehouseid, count(*) as c from vw_items where mediatitle like '%PGM%' group by mediafilehouseid order by c desc + +select concat('''', concat(mediafilehouseid,''',')), count(*) as c from vw_items group by mediafilehouseid order by c desc -select mediatitle, count(*) from vw_items where itemtitle like '%Echo TV%' and itemtitle like '2%' and mediatitle like '%PGM%' group by mediatitle +select mediatitle, count(*) from vw_items where itemtitle like '%Echo TV%' and itemtitle like '2%' and mediatitle like '%PGM%' and mediatitle like '%napiakt%' group by mediatitle select * from vw_items where itemtitle like 'MC-%' and mediahouseid like 'MC-%' select count(distinct(mediafilehouseid)) from vw_items where itemtitle like 'MC-%' and mediahouseid like 'MC-%' +select mediafilehouseid, count(*) as c from vw_items where itemtitle like 'MC-%' and mediahouseid like 'MC-%' group by mediafilehouseid order by c desc +select mediafilehouseid, count(*) as c from vw_items group by mediafilehouseid order by c desc select * from vw_items where mediafilehouseid not like 'M%' and mediahouseid not like 'P%' and mediahouseid not like 'R%' and mediafilehouseid not like 'MC%' and itemtitle not like '%Echo TV%' and itemtitle not like '2%' and mediatitle not like '%CLN%' and mediatitle not like '%PGM%' and itemhouseid != itemtitle @@ -21,4 +230,20 @@ and itemtitle not like '%Echo TV%' and itemtitle not like '2%' and mediatitle no select count(distinct(mediafilehouseid)) from vw_items where mediafilehouseid not like 'M%' and mediahouseid not like 'P%' and mediahouseid not like 'R%' and mediafilehouseid not like 'MC%' and itemtitle not like '%Echo TV%' and itemtitle not like '2%' and mediatitle not like '%CLN%' and mediatitle not like '%PGM%' and itemhouseid != itemtitle +--Hosszak +select * from vw_items where itemtitle like '%Echo TV%' and itemtitle like '2%' and mediatitle like '%PGM%' +select i.title, sum(length(m.description)) from media m, item i where i.id=m.itemid and ITEMID in ( +select id from item where title like '2%' and title like '%Echo%' +) group by i.title order by i.title desc + +select i.title, sum(length(m.description)) as l from media m, item i where i.id=m.itemid and ITEMID in ( +select id from item where title like '2%' and title like '%Echo%' +) group by i.title order by l desc + +select * from item where title='2018.01.24 22:00 Híradó Echo TV' +select * from media where itemid = 32962 + +select mediafilehouseid, count(*) as c from vw_items where mediatitle like '%PGM%' group by mediafilehouseid order by c desc +--Napi duplikátum +select mediafilehouseid, count(*) as c from vw_items where archived like '%02.%' group by mediafilehouseid order by c desc diff --git a/server/user.jobengine.osgi.db/sql/stat-rd.sql b/server/user.jobengine.osgi.db/sql/stat-rd.sql new file mode 100644 index 00000000..8bc191f3 --- /dev/null +++ b/server/user.jobengine.osgi.db/sql/stat-rd.sql @@ -0,0 +1,14 @@ +create view vw_items_rd as +select *, replace(mediafilehouseid, concat('-', itemhouseid), '') as filename from vw_items where itemtitle like '%Echo TV%' and itemtitle like '2%' and mediatitle not like '%CLN%' and mediatitle not like '%PGM%' + +select * from vw_items_rd + +select count(*) from vw_items_rd + +select filename, count(*) as c from vw_items_rd group by filename order by c desc + +select distinct(concat('''', concat(mediafilehouseid,''','))) from vw_items_rd + +select itemtitle, mediafilehouseid, filename, archived from vw_items_rd where mediafilehouseid in ( + select distinct(mediafilehouseid) from vw_items_rd +) order by filename diff --git a/server/user.jobengine.osgi.server/src/user/jobengine/server/JobRuntime.java b/server/user.jobengine.osgi.server/src/user/jobengine/server/JobRuntime.java index b0247fcd..080be740 100644 --- a/server/user.jobengine.osgi.server/src/user/jobengine/server/JobRuntime.java +++ b/server/user.jobengine.osgi.server/src/user/jobengine/server/JobRuntime.java @@ -569,18 +569,8 @@ public class JobRuntime extends Job implements IJobRuntime { } private void signal(SignalType signalType) { - if (jobEngine != null) { + if (jobEngine != null) jobEngine.fireJobChangedEvent(new JobChangedEvent(this, signalType)); - /* - IItemManager manager = jobEngine.getItemManager(); - if (manager != null) { - manager.signal(getId(), signalType, StaticTables.JOB); - logger.debug("signal " + signalType + ", " + toString()); - if (signalType == SignalType.UPDATE) - modify(); - } - */ - } } @Override diff --git a/server/user.jobengine.osgi.server/src/user/jobengine/server/ast/InputParameter.java b/server/user.jobengine.osgi.server/src/user/jobengine/server/ast/InputParameter.java index ce8de825..c413f319 100644 --- a/server/user.jobengine.osgi.server/src/user/jobengine/server/ast/InputParameter.java +++ b/server/user.jobengine.osgi.server/src/user/jobengine/server/ast/InputParameter.java @@ -1,7 +1,7 @@ package user.jobengine.server.ast; /** - * Bementi paraméter osztály. + * Bementi param�ter oszt�ly. */ public class InputParameter extends Parameter { @@ -11,31 +11,33 @@ public class InputParameter extends Parameter { } /** - * Példányosítás kifejezés megadásával. - * + * P�ld�nyos�t�s kifejez�s megad�s�val. + * * @param expression - * Kifejezés. + * Kifejez�s. */ public InputParameter(Expression expression) { this.expression = expression; } /** - * Kifejezés lekérdezése. - * - * @return Kifejezés. + * Kifejez�s lek�rdez�se. + * + * @return Kifejez�s. */ public Expression getExpression() { return expression; } /** - * Kifejezés beállítása. - * + * Kifejez�s be�ll�t�sa. + * * @param expression - * Kifejezés. + * Kifejez�s. */ public void setExpression(Expression expression) { + if (this.expression != null) + throw new RuntimeException("Malformed step parameter template. Missing tag. It is required for all inputs."); this.expression = expression; } diff --git a/server/user.jobengine.osgi.server/src/user/jobengine/server/steps/EscortFiles.java b/server/user.jobengine.osgi.server/src/user/jobengine/server/steps/EscortFiles.java index 3c5e6072..48f99b61 100644 --- a/server/user.jobengine.osgi.server/src/user/jobengine/server/steps/EscortFiles.java +++ b/server/user.jobengine.osgi.server/src/user/jobengine/server/steps/EscortFiles.java @@ -115,7 +115,7 @@ public class EscortFiles { try { Files.createDirectories(filePath, attr); } catch (Exception e) { - logger.catching(e); + //logger.catching(e); try { Files.createDirectory(filePath); } catch (Exception e1) { diff --git a/server/user.jobengine.osgi.server/src/user/jobengine/server/steps/JobStep.java b/server/user.jobengine.osgi.server/src/user/jobengine/server/steps/JobStep.java index 1b3bbb02..4338eeb4 100644 --- a/server/user.jobengine.osgi.server/src/user/jobengine/server/steps/JobStep.java +++ b/server/user.jobengine.osgi.server/src/user/jobengine/server/steps/JobStep.java @@ -72,6 +72,7 @@ public class JobStep implements IJobStep { extendedInputs = addParameter(method, extendedInputs, IJobEngine.class, jobEngine); extendedInputs = addParameter(method, extendedInputs, IJobRuntime.class, jobRuntime); result = (Object[]) method.invoke(this, extendedInputs); + break; } } return result; diff --git a/server/user.jobengine.osgi.server/test/user/jobengine/server/ast/ParserTest.java b/server/user.jobengine.osgi.server/test/user/jobengine/server/ast/ParserTest.java index e2ccbeb8..18839de1 100644 --- a/server/user.jobengine.osgi.server/test/user/jobengine/server/ast/ParserTest.java +++ b/server/user.jobengine.osgi.server/test/user/jobengine/server/ast/ParserTest.java @@ -25,15 +25,21 @@ import user.jobengine.server.instructions.PushToStackInstruction; import user.jobengine.server.instructions.SendMessageToUserInstruction; public class ParserTest { + @BeforeClass + public static void setUpClass() throws Exception { + } + private Parser sut = null; private Encoder encoder = null; private IProgram expected = null; + private IProgram encoded = null; - - @BeforeClass - public static void setUpClass() throws Exception { + + void exercise(String xml) throws Exception { + sut = new Parser(new ByteArrayInputStream(xml.getBytes())); + encoded = (IProgram) encoder.visitJobTemplate(sut.parse(), null); } - + @Before public void setup() throws Exception { expected = new Program(); @@ -43,60 +49,22 @@ public class ParserTest { @After public void tearDown() throws Exception { } - - void exercise(String xml) throws Exception { - sut = new Parser(new ByteArrayInputStream(xml.getBytes())); - encoded = (IProgram) encoder.visitJobTemplate(sut.parse(), null); - } @Test - public void testParseParameterDeclaration() throws Exception { - // Fixture - expected.addInstruction(new PushToStackInstruction("p1")); - expected.addInstruction(new PushToStackInstruction(java.lang.String.class)); - expected.addInstruction(new CheckParameterInstruction()); - - final String xml = - "" + - "" + - "" + - "" + - ""+ - "" + - "" + - ""; - - // Exercise - exercise(xml); - - // Verify - assertEquals(expected, encoded); - } - - @Test - public void testParseVariableDeclaration() throws Exception { - // Fixture - expected.addInstruction(new PushToStackInstruction("v1")); - expected.addInstruction(new PushToStackInstruction(java.lang.Integer.class)); - expected.addInstruction(new DeclareVariableInstruction()); - - final String xml = - "" + - "" + - "" + - "" + - ""+ - "" + - "" + - ""; - - // Exercise - exercise(xml); - - // Verify - assertEquals(expected, encoded); + public void testDigester() throws Exception { + String path = DirectoryUtils.checkSlash(System.getProperty("user.dir"), false, true); + String file = path + "..\\user.jobengine.executors\\jobtemplates\\test-multiparam.xml"; + InputStream is = new FileInputStream(file); + Parser parser = new Parser(is); + JobTemplate template = parser.parse(); + is.close(); + assertNotNull(template); + + IProgram program = (IProgram) encoder.visitJobTemplate(template, null); + assertNotNull(program); + } - + @Test public void testParseCallJobStep() throws Exception { // Fixture @@ -111,36 +79,32 @@ public class ParserTest { expected.addInstruction(new CallJobStepInstruction()); expected.addInstruction(new AssignVariableInstruction()); expected.addInstruction(new AssignVariableInstruction()); - - final String xml = - "" + - "" + - "" + - "" + - "" + - "" + - "" + - "" + - "" + - "" + - "" + - "" + - "" + - "" + - "" + - "" + - "" + - "" + - "" + - "" + - "" + - "" + - ""; - - + + final String xml = "" + "" + "" + "" + + "" + "" + "" + "" + "" + "" + "" + "" + + "" + "" + "" + "" + "" + "" + "" + "" + + "" + "" + ""; + // Exercise exercise(xml); - + + // Verify + assertEquals(expected, encoded); + } + + @Test + public void testParseParameterDeclaration() throws Exception { + // Fixture + expected.addInstruction(new PushToStackInstruction("p1")); + expected.addInstruction(new PushToStackInstruction(java.lang.String.class)); + expected.addInstruction(new CheckParameterInstruction()); + + final String xml = "" + "" + "" + "" + + "" + "" + "" + ""; + + // Exercise + exercise(xml); + // Verify assertEquals(expected, encoded); } @@ -158,49 +122,34 @@ public class ParserTest { expected.addInstruction(new PushToStackInstruction(1)); expected.addInstruction(new SendMessageToUserInstruction()); expected.addInstruction(new AssignVariableInstruction()); - - final String xml = - "" + - "" + - "" + - "" + - "" + - "" + - "" + - "" + - "" + - "" + - "" + - "" + - "" + - "" + - "" + - "" + - "" + - "" + - "" + - ""; - + + final String xml = "" + "" + "" + + "" + "" + "" + "" + "" + + "" + "" + "" + "" + "" + "" + "" + "" + + "" + "" + "" + ""; + // Exercise exercise(xml); - + // Verify assertEquals(expected, encoded); } - + @Test - public void testDigester() throws Exception { - String path = DirectoryUtils.checkSlash(System.getProperty("user.dir"), false, true); - String file = path + "resources\\templates\\program1.xml"; - InputStream is = new FileInputStream(file); - Parser parser = new Parser(is); - JobTemplate template = parser.parse(); - is.close(); - assertNotNull(template); - - IProgram program = (IProgram) encoder.visitJobTemplate(template, null); - assertNotNull(program); - - } - + public void testParseVariableDeclaration() throws Exception { + // Fixture + expected.addInstruction(new PushToStackInstruction("v1")); + expected.addInstruction(new PushToStackInstruction(java.lang.Integer.class)); + expected.addInstruction(new DeclareVariableInstruction()); + + final String xml = "" + "" + "" + "" + + "" + "" + "" + ""; + + // Exercise + exercise(xml); + + // Verify + assertEquals(expected, encoded); + } + } -- 2.54.0