View Javadoc
1   package de.dlr.shepard.neo4Core.export;
2   
3   import java.io.ByteArrayInputStream;
4   import java.io.ByteArrayOutputStream;
5   import java.io.File;
6   import java.io.IOException;
7   import java.io.InputStream;
8   import java.time.LocalDateTime;
9   import java.time.ZoneId;
10  import java.time.format.DateTimeFormatter;
11  import java.util.ArrayList;
12  import java.util.Date;
13  import java.util.List;
14  import java.util.zip.ZipEntry;
15  import java.util.zip.ZipOutputStream;
16  
17  import com.fasterxml.jackson.databind.ObjectMapper;
18  
19  import de.dlr.shepard.neo4Core.entities.Collection;
20  import de.dlr.shepard.neo4Core.entities.DataObject;
21  import de.dlr.shepard.neo4Core.entities.User;
22  import de.dlr.shepard.neo4Core.io.AbstractDataObjectIO;
23  import de.dlr.shepard.neo4Core.io.BasicEntityIO;
24  import de.dlr.shepard.neo4Core.io.BasicReferenceIO;
25  import de.dlr.shepard.neo4Core.io.CollectionIO;
26  import de.dlr.shepard.neo4Core.io.DataObjectIO;
27  import edu.kit.datamanager.ro_crate.RoCrate.RoCrateBuilder;
28  import edu.kit.datamanager.ro_crate.entities.contextual.PersonEntity;
29  import edu.kit.datamanager.ro_crate.entities.data.FileEntity;
30  import edu.kit.datamanager.ro_crate.entities.data.FileEntity.FileEntityBuilder;
31  import lombok.AccessLevel;
32  import lombok.Getter;
33  import lombok.extern.slf4j.Slf4j;
34  
35  @Slf4j
36  public class ExportBuilder {
37  
38  	private ObjectMapper objectMapper = new ObjectMapper();
39  
40  	@Getter(value = AccessLevel.PROTECTED)
41  	private RoCrateBuilder builder;
42  	private ByteArrayOutputStream baos;
43  	private ZipOutputStream zos;
44  	private List<String> entries;
45  
46  	public ExportBuilder(Collection collection) throws IOException {
47  		baos = new ByteArrayOutputStream();
48  		zos = new ZipOutputStream(baos);
49  		entries = new ArrayList<>();
50  
51  		var roCrateName = collection.getName() + " Research Object Crate";
52  		var roCrateDescription = "Research Object Crate representing the shepard Collection " + collection.getName();
53  		builder = new RoCrateBuilder(roCrateName, roCrateDescription);
54  
55  		var collectionEntity = createFileEntity(new CollectionIO(collection));
56  		builder.addDataEntity(collectionEntity);
57  		addPersonEntity(collection.getCreatedBy());
58  	}
59  
60  	public ExportBuilder addDataObject(DataObject dataObject) throws IOException {
61  		var dataObjectEntity = createFileEntity(new DataObjectIO(dataObject));
62  		builder.addDataEntity(dataObjectEntity);
63  		addPersonEntity(dataObject.getCreatedBy());
64  		return this;
65  	}
66  
67  	public ExportBuilder addReference(BasicReferenceIO reference, User author) throws IOException {
68  		var referenceEntity = createFileEntity(reference);
69  		builder.addDataEntity(referenceEntity);
70  		addPersonEntity(author);
71  		return this;
72  	}
73  
74  	public ExportBuilder addPayload(byte[] payload, String filename, String name) throws IOException {
75  		addToZip(filename, payload);
76  		builder.addDataEntity(createFileEntityBuilder(filename, name).build());
77  		return this;
78  	}
79  
80  	public ExportBuilder addPayload(byte[] payload, String filename, String name, String encodingFormat)
81  			throws IOException {
82  		addToZip(filename, payload);
83  		builder.addDataEntity(createFileEntityBuilder(filename, name, encodingFormat).build());
84  		return this;
85  	}
86  
87  	private void addPersonEntity(User user) {
88  		if (user == null)
89  			return;
90  
91  		var entity = new PersonEntity.PersonEntityBuilder().setId(user.getUsername()).setEmail(user.getEmail())
92  				.setGivenName(user.getFirstName()).setFamilyName(user.getLastName()).build();
93  
94  		builder.addContextualEntity(entity);
95  	}
96  
97  	public InputStream build() throws IOException {
98  		var roCrate = builder.build();
99  		Object jsonObject = objectMapper.readValue(roCrate.getJsonMetadata(), Object.class);
100 		byte[] bytes = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsBytes(jsonObject);
101 		addToZip(ExportConstants.ROCRATE_METADATA, bytes);
102 		zos.close();
103 
104 		return new ByteArrayInputStream(baos.toByteArray());
105 	}
106 
107 	private FileEntity createFileEntity(AbstractDataObjectIO entity) throws IOException {
108 		var filename = entity.getId() + ExportConstants.JSON_FILE_EXTENSION;
109 		writeEntityToZip(filename, entity);
110 
111 		var fileEntityBuilder = createFileEntityBuilder(entity, filename);
112 		fileEntityBuilder.addProperty(ExportConstants.DESCRIPTION_PROP, entity.getDescription());
113 		return fileEntityBuilder.build();
114 	}
115 
116 	private FileEntity createFileEntity(BasicReferenceIO entity) throws IOException {
117 		var filename = entity.getId() + ExportConstants.JSON_FILE_EXTENSION;
118 		writeEntityToZip(filename, entity);
119 
120 		var fileEntityBuilder = createFileEntityBuilder(entity, filename);
121 		fileEntityBuilder.addProperty(ExportConstants.TYPE_PROP, entity.getType());
122 		return fileEntityBuilder.build();
123 	}
124 
125 	private void writeEntityToZip(String filename, Object entity) throws IOException {
126 		var bytes = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsBytes(entity);
127 		addToZip(filename, bytes);
128 	}
129 
130 	private static FileEntityBuilder createFileEntityBuilder(String filename, String name) {
131 		// We need a dummy file since the FileEntityBuilder cannot work without a source
132 		// file. We only use the RoCrate lib just for creating the
133 		// ro-crate-metadata.json, so there is no file needed at this point.
134 		var dummyFile = new File("dummy.txt");
135 		return new FileEntity.FileEntityBuilder().setId(filename).setSource(dummyFile)
136 				.addProperty(ExportConstants.NAME_PROP, name);
137 	}
138 
139 	private static FileEntityBuilder createFileEntityBuilder(String filename, String name, String encodingFormat) {
140 		var fileEntityBuilder = createFileEntityBuilder(filename, name);
141 		fileEntityBuilder.addProperty("encodingFormat", encodingFormat);
142 		return fileEntityBuilder;
143 	}
144 
145 	private static FileEntityBuilder createFileEntityBuilder(BasicEntityIO entity, String filename) {
146 		var createdAt = convertToIsoDate(entity.getCreatedAt());
147 		var type = entity.getClass().getSimpleName().replace("IO", "");
148 
149 		var fileEntityBuilder = createFileEntityBuilder(filename, entity.getName(), "application/json");
150 		fileEntityBuilder.addAuthor(entity.getCreatedBy()).addProperty(ExportConstants.CREATED_PROP, createdAt)
151 				.addProperty(ExportConstants.TYPE_PROP, type);
152 
153 		if (entity.getUpdatedAt() != null)
154 			fileEntityBuilder.addProperty(ExportConstants.UPDATED_PROP, convertToIsoDate(entity.getUpdatedAt()));
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.warn("{} 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 
176 }