1 package de.dlr.shepard.neo4Core.export; 2 3 import com.fasterxml.jackson.databind.ObjectMapper; 4 import de.dlr.shepard.neo4Core.entities.Collection; 5 import de.dlr.shepard.neo4Core.entities.DataObject; 6 import de.dlr.shepard.neo4Core.entities.User; 7 import de.dlr.shepard.neo4Core.io.AbstractDataObjectIO; 8 import de.dlr.shepard.neo4Core.io.BasicEntityIO; 9 import de.dlr.shepard.neo4Core.io.BasicReferenceIO; 10 import de.dlr.shepard.neo4Core.io.CollectionIO; 11 import de.dlr.shepard.neo4Core.io.DataObjectIO; 12 import edu.kit.datamanager.ro_crate.RoCrate.RoCrateBuilder; 13 import edu.kit.datamanager.ro_crate.entities.contextual.PersonEntity; 14 import edu.kit.datamanager.ro_crate.entities.data.FileEntity; 15 import edu.kit.datamanager.ro_crate.entities.data.FileEntity.FileEntityBuilder; 16 import io.quarkus.logging.Log; 17 import java.io.ByteArrayInputStream; 18 import java.io.ByteArrayOutputStream; 19 import java.io.IOException; 20 import java.io.InputStream; 21 import java.time.LocalDateTime; 22 import java.time.ZoneId; 23 import java.time.format.DateTimeFormatter; 24 import java.util.ArrayList; 25 import java.util.Date; 26 import java.util.List; 27 import java.util.zip.ZipEntry; 28 import java.util.zip.ZipOutputStream; 29 import lombok.AccessLevel; 30 import lombok.Getter; 31 32 public class ExportBuilder { 33 34 private ObjectMapper objectMapper = new ObjectMapper(); 35 36 @Getter(value = AccessLevel.PROTECTED) 37 private RoCrateBuilder builder; 38 39 private ByteArrayOutputStream baos; 40 private ZipOutputStream zos; 41 private List<String> entries; 42 43 public ExportBuilder(Collection collection) throws IOException { 44 baos = new ByteArrayOutputStream(); 45 zos = new ZipOutputStream(baos); 46 entries = new ArrayList<>(); 47 48 var roCrateName = collection.getName() + " Research Object Crate"; 49 var roCrateDescription = "Research Object Crate representing the shepard Collection " + collection.getName(); 50 51 builder = new RoCrateBuilder().addName(roCrateName).addDescription(roCrateDescription); 52 53 var collectionEntity = createFileEntity(new CollectionIO(collection)); 54 builder.addDataEntity(collectionEntity); 55 addPersonEntity(collection.getCreatedBy()); 56 } 57 58 public ExportBuilder addDataObject(DataObject dataObject) throws IOException { 59 var dataObjectEntity = createFileEntity(new DataObjectIO(dataObject)); 60 builder.addDataEntity(dataObjectEntity); 61 addPersonEntity(dataObject.getCreatedBy()); 62 return this; 63 } 64 65 public ExportBuilder addReference(BasicReferenceIO reference, User author) throws IOException { 66 var referenceEntity = createFileEntity(reference); 67 builder.addDataEntity(referenceEntity); 68 addPersonEntity(author); 69 return this; 70 } 71 72 public ExportBuilder addPayload(byte[] payload, String filename, String name) throws IOException { 73 addToZip(filename, payload); 74 builder.addDataEntity(createFileEntityBuilder(filename, name).build()); 75 return this; 76 } 77 78 public ExportBuilder addPayload(byte[] payload, String filename, String name, String encodingFormat) 79 throws IOException { 80 addToZip(filename, payload); 81 builder.addDataEntity(createFileEntityBuilder(filename, name, encodingFormat).build()); 82 return this; 83 } 84 85 private void addPersonEntity(User user) { 86 if (user == null) return; 87 88 var entity = new PersonEntity.PersonEntityBuilder() 89 .setId(user.getUsername()) 90 .setEmail(user.getEmail()) 91 .setGivenName(user.getFirstName()) 92 .setFamilyName(user.getLastName()) 93 .build(); 94 95 builder.addContextualEntity(entity); 96 } 97 98 public InputStream build() throws IOException { 99 var roCrate = builder.build(); 100 Object jsonObject = objectMapper.readValue(roCrate.getJsonMetadata(), Object.class); 101 byte[] bytes = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsBytes(jsonObject); 102 addToZip(ExportConstants.ROCRATE_METADATA, bytes); 103 zos.close(); 104 105 return new ByteArrayInputStream(baos.toByteArray()); 106 } 107 108 private FileEntity createFileEntity(AbstractDataObjectIO entity) throws IOException { 109 var filename = entity.getId() + ExportConstants.JSON_FILE_EXTENSION; 110 writeEntityToZip(filename, entity); 111 112 var fileEntityBuilder = createFileEntityBuilder(entity, filename); 113 fileEntityBuilder.addProperty(ExportConstants.DESCRIPTION_PROP, entity.getDescription()); 114 return fileEntityBuilder.build(); 115 } 116 117 private FileEntity createFileEntity(BasicReferenceIO entity) throws IOException { 118 var filename = entity.getId() + ExportConstants.JSON_FILE_EXTENSION; 119 writeEntityToZip(filename, entity); 120 121 var fileEntityBuilder = createFileEntityBuilder(entity, filename); 122 fileEntityBuilder.addProperty(ExportConstants.TYPE_PROP, entity.getType()); 123 return fileEntityBuilder.build(); 124 } 125 126 private void writeEntityToZip(String filename, Object entity) throws IOException { 127 var bytes = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsBytes(entity); 128 addToZip(filename, bytes); 129 } 130 131 private static FileEntityBuilder createFileEntityBuilder(String filename, String name) { 132 return new FileEntity.FileEntityBuilder().setId(filename).addProperty(ExportConstants.NAME_PROP, name); 133 } 134 135 private static FileEntityBuilder createFileEntityBuilder(String filename, String name, String encodingFormat) { 136 var fileEntityBuilder = createFileEntityBuilder(filename, name); 137 fileEntityBuilder.addProperty("encodingFormat", encodingFormat); 138 return fileEntityBuilder; 139 } 140 141 private static FileEntityBuilder createFileEntityBuilder(BasicEntityIO entity, String filename) { 142 var createdAt = convertToIsoDate(entity.getCreatedAt()); 143 var type = entity.getClass().getSimpleName().replace("IO", ""); 144 145 var fileEntityBuilder = createFileEntityBuilder(filename, entity.getName(), "application/json"); 146 fileEntityBuilder 147 .addAuthor(entity.getCreatedBy()) 148 .addProperty(ExportConstants.CREATED_PROP, createdAt) 149 .addProperty(ExportConstants.TYPE_PROP, type); 150 151 if (entity.getUpdatedAt() != null) fileEntityBuilder.addProperty( 152 ExportConstants.UPDATED_PROP, 153 convertToIsoDate(entity.getUpdatedAt()) 154 ); 155 156 return fileEntityBuilder; 157 } 158 159 private static String convertToIsoDate(Date dateToConvert) { 160 var localDateTime = LocalDateTime.ofInstant(dateToConvert.toInstant(), ZoneId.systemDefault()); 161 return DateTimeFormatter.ISO_DATE_TIME.format(localDateTime); 162 } 163 164 private void addToZip(String filename, byte[] bytes) throws IOException { 165 if (entries.contains(filename)) { 166 Log.warnf("%s already in zip file, skipping", filename); 167 return; 168 } 169 170 zos.putNextEntry(new ZipEntry(filename)); 171 zos.write(bytes); 172 zos.closeEntry(); 173 entries.add(filename); 174 } 175 }