From a125e227d103ba6944889cf44426c99b962de12b Mon Sep 17 00:00:00 2001 From: Piotr Gawron <piotr.gawron@uni.lu> Date: Wed, 4 Oct 2017 17:23:27 +0200 Subject: [PATCH] upload of zip project is possible --- .gitignore | 1 + .../converter/ComplexZipConverter.java | 678 ++-- .../converter/ComplexZipConverterParams.java | 217 +- .../lcsb/mapviewer/converter/AllTests.java | 7 +- .../ComplexZipConverterParamsTest.java | 22 + frontend-js/.gitignore | 2 + frontend-js/src/main/js/ServerConnector.js | 132 +- .../src/main/js/gui/admin/AddProjectDialog.js | 14 +- .../src/test/js/ServerConnector-test.js | 217 +- .../js/gui/admin/AddProjectDialog-test.js | 3 +- .../POST_token=MOCK_TOKEN_ID& | 7 + .../POST_token=MOCK_TOKEN_ID& | 7 + ...ame=test.txt&length=5&token=MOCK_TOKEN_ID& | 7 + ...me=test.xml&length=13&token=MOCK_TOKEN_ID& | 7 + .../model/cache/UploadedFileEntry.java | 70 +- persist/src/db/11.1.0/fix_db_20171004.sql | 12 + .../java/lcsb/mapviewer/api/BaseRestImpl.java | 517 +-- .../mapviewer/api/files/FileController.java | 59 + .../mapviewer/api/files/FileRestImpl.java | 107 + .../api/projects/ProjectRestImpl.java | 42 +- .../resources/applicationContext-rest.xml | 2 + .../java/lcsb/mapviewer/api/AllRestTests.java | 8 +- .../lcsb/mapviewer/api/RestTestFunctions.java | 492 +-- .../mapviewer/api/files/AllFileTests.java | 11 + .../mapviewer/api/files/FileRestImplTest.java | 80 + .../mapviewer/services/SecurityException.java | 20 +- .../services/impl/LayoutService.java | 4 + .../services/impl/ProjectService.java | 2926 +++++++++-------- 28 files changed, 3081 insertions(+), 2590 deletions(-) create mode 100644 converter/src/test/java/lcsb/mapviewer/converter/ComplexZipConverterParamsTest.java create mode 100644 frontend-js/testFiles/apiCalls/files/6790.uploadContent/POST_token=MOCK_TOKEN_ID& create mode 100644 frontend-js/testFiles/apiCalls/files/6791.uploadContent/POST_token=MOCK_TOKEN_ID& create mode 100644 frontend-js/testFiles/apiCalls/files/POST_filename=test.txt&length=5&token=MOCK_TOKEN_ID& create mode 100644 frontend-js/testFiles/apiCalls/files/POST_filename=test.xml&length=13&token=MOCK_TOKEN_ID& create mode 100644 persist/src/db/11.1.0/fix_db_20171004.sql create mode 100644 rest-api/src/main/java/lcsb/mapviewer/api/files/FileController.java create mode 100644 rest-api/src/main/java/lcsb/mapviewer/api/files/FileRestImpl.java create mode 100644 rest-api/src/test/java/lcsb/mapviewer/api/files/AllFileTests.java create mode 100644 rest-api/src/test/java/lcsb/mapviewer/api/files/FileRestImplTest.java diff --git a/.gitignore b/.gitignore index a402933690..bbcea90463 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,4 @@ service/minerva-big/ npm-debug.log .idea/workspace.xml +.idea/libraries/ diff --git a/converter/src/main/java/lcsb/mapviewer/converter/ComplexZipConverter.java b/converter/src/main/java/lcsb/mapviewer/converter/ComplexZipConverter.java index dc91e510ca..e2640f0b3e 100644 --- a/converter/src/main/java/lcsb/mapviewer/converter/ComplexZipConverter.java +++ b/converter/src/main/java/lcsb/mapviewer/converter/ComplexZipConverter.java @@ -52,355 +52,365 @@ import lcsb.mapviewer.model.map.species.Species; */ public class ComplexZipConverter { - /** - * Size of the buffer used for accessing single chunk of data from input - * stream. - */ - private static final int BUFFER_SIZE = 1024; + /** + * Size of the buffer used for accessing single chunk of data from input stream. + */ + private static final int BUFFER_SIZE = 1024; - /** - * Default class logger. - */ - private static Logger logger = Logger.getLogger(ComplexZipConverter.class); + /** + * Default class logger. + */ + private static Logger logger = Logger.getLogger(ComplexZipConverter.class); - /** - * Class used to create single submap from a file. - */ - private Class<? extends IConverter> converterClazz; + /** + * Class used to create single submap from a file. + */ + private Class<? extends IConverter> converterClazz; - /** - * Default constructor. Checks if the class given in the parameter is proper - * {@link IConverter} implementation. - * - * @param clazz - * {@link IConverter} class used for creation of the single submap - */ - public ComplexZipConverter(Class<? extends IConverter> clazz) { - if (Modifier.isAbstract(clazz.getModifiers())) { - throw new InvalidClassException("Param class cannot be abstract"); - } + /** + * Default constructor. Checks if the class given in the parameter is proper + * {@link IConverter} implementation. + * + * @param clazz + * {@link IConverter} class used for creation of the single submap + */ + public ComplexZipConverter(Class<? extends IConverter> clazz) { + if (Modifier.isAbstract(clazz.getModifiers())) { + throw new InvalidClassException("Param class cannot be abstract"); + } - if (Modifier.isInterface(clazz.getModifiers())) { - throw new InvalidClassException("Param class cannot be an interface"); - } + if (Modifier.isInterface(clazz.getModifiers())) { + throw new InvalidClassException("Param class cannot be an interface"); + } - converterClazz = clazz; - } + converterClazz = clazz; + } - /** - * Creates complex {@link Model} that contains submaps. - * - * @param params - * {@link ComplexZipConverterParams object} with information about - * data from which result is going to be created - * @return complex {@link Model} created from input data - * @throws InvalidInputDataExecption - * thrown when there is a problem with accessing input data - */ - public Model createModel(ComplexZipConverterParams params) throws InvalidInputDataExecption { - try { - ZipFile zipFile = params.getZipFile(); - Enumeration<? extends ZipEntry> entries; - String mapping = validateSubmodelInformation(params, zipFile); + /** + * Creates complex {@link Model} that contains submaps. + * + * @param params + * {@link ComplexZipConverterParams object} with information about data + * from which result is going to be created + * @return complex {@link Model} created from input data + * @throws InvalidInputDataExecption + * thrown when there is a problem with accessing input data + */ + public Model createModel(ComplexZipConverterParams params) throws InvalidInputDataExecption { + try { + ZipFile zipFile = params.getZipFile(); + Enumeration<? extends ZipEntry> entries; + String mapping = validateSubmodelInformation(params, zipFile); - IConverter converter = createConverterInstance(); - Map<String, Model> filenameModelMap = new HashMap<String, Model>(); - Map<String, Model> nameModelMap = new HashMap<String, Model>(); + IConverter converter = createConverterInstance(); + Map<String, Model> filenameModelMap = new HashMap<String, Model>(); + Map<String, Model> nameModelMap = new HashMap<String, Model>(); - entries = zipFile.entries(); - Model result = null; - List<Layout> layouts = new ArrayList<Layout>(); - List<DataMiningSet> dataMiningSets = new ArrayList<>(); - while (entries.hasMoreElements()) { - ZipEntry entry = entries.nextElement(); - if (!entry.isDirectory()) { - ZipEntryFile zef = params.getEntry(entry.getName()); - if (zef instanceof ModelZipEntryFile) { - ModelZipEntryFile modelEntryFile = (ModelZipEntryFile) zef; - InputStream is = zipFile.getInputStream(entry); - ConverterParams cParams = new ConverterParams(); - cParams.inputStream(is); - Model model = converter.createModel(cParams); - model.setName(modelEntryFile.getName()); - filenameModelMap.put(entry.getName(), model); - nameModelMap.put(FilenameUtils.getBaseName(modelEntryFile.getFilename()), model); - if (modelEntryFile.isRoot()) { - result = model; - } - } else if (zef instanceof LayoutZipEntryFile) { - layouts.add(layoutZipEntryFileToLayout(params, zipFile, entry, (LayoutZipEntryFile) zef)); - } else if (zef instanceof ImageZipEntryFile) { - continue; - // imageEntries.add((ImageZipEntryFile) zef); - } else if (zef instanceof DataMiningZipEntryFile) { - dataMiningSets.add(dataMiningZipEntryToDataMiningSet(zipFile, entry, (DataMiningZipEntryFile) zef)); - } else { - throw new NotImplementedException("Unknwon entry type: " + zef.getClass()); - } - } - } + entries = zipFile.entries(); + Model result = null; + List<Layout> layouts = new ArrayList<Layout>(); + List<DataMiningSet> dataMiningSets = new ArrayList<>(); + while (entries.hasMoreElements()) { + ZipEntry entry = entries.nextElement(); + if (!entry.isDirectory()) { + ZipEntryFile zef = params.getEntry(entry.getName()); + if (zef instanceof ModelZipEntryFile) { + ModelZipEntryFile modelEntryFile = (ModelZipEntryFile) zef; + InputStream is = zipFile.getInputStream(entry); + ConverterParams cParams = new ConverterParams(); + cParams.inputStream(is); + Model model = converter.createModel(cParams); + model.setName(modelEntryFile.getName()); + filenameModelMap.put(entry.getName(), model); + nameModelMap.put(FilenameUtils.getBaseName(modelEntryFile.getFilename()), model); + if (modelEntryFile.isRoot()) { + result = model; + } + } else if (zef instanceof LayoutZipEntryFile) { + layouts.add(layoutZipEntryFileToLayout(params, zipFile, entry, (LayoutZipEntryFile) zef)); + } else if (zef instanceof ImageZipEntryFile) { + continue; + // imageEntries.add((ImageZipEntryFile) zef); + } else if (zef instanceof DataMiningZipEntryFile) { + dataMiningSets.add(dataMiningZipEntryToDataMiningSet(zipFile, entry, (DataMiningZipEntryFile) zef)); + } else { + throw new NotImplementedException("Unknwon entry type: " + zef.getClass()); + } + } + } - result.addDataMiningSets(dataMiningSets); + result.addDataMiningSets(dataMiningSets); - for (Entry<String, Model> entry : filenameModelMap.entrySet()) { - String filename = entry.getKey(); - Model model = entry.getValue(); - ZipEntryFile zef = params.getEntry(filename); - if (zef instanceof ModelZipEntryFile) { - ModelZipEntryFile modelEntryFile = (ModelZipEntryFile) zef; - if (!modelEntryFile.isRoot() && !modelEntryFile.isMappingFile()) { - ModelSubmodelConnection submodel = new ModelSubmodelConnection(model, modelEntryFile.getType()); - submodel.setName(modelEntryFile.getName()); - result.addSubmodelConnection(submodel); - } - } - } - for (Layout layout : layouts) { - Layout topLayout = new Layout(layout); - result.addLayout(topLayout); - int modelId = 0; - topLayout.setDirectory(topLayout.getDirectory() + modelId); - modelId++; - for (ModelSubmodelConnection connection : result.getSubmodelConnections()) { - Layout childLayout = new Layout(layout); - // we need to set separate directory names for different submodels - childLayout.setDirectory(childLayout.getDirectory() + modelId); - modelId++; - childLayout.setParentLayout(topLayout); - connection.getSubmodel().addLayout(childLayout); - } - } - Model mappingModel = filenameModelMap.get(mapping); - if (mappingModel != null) { - for (Reaction reaction : mappingModel.getReactions()) { - processReaction(mapping, nameModelMap, result, reaction); - } - } - return result; - } catch (IOException e) { - throw new InvalidArgumentException(e); - } - } + for (Entry<String, Model> entry : filenameModelMap.entrySet()) { + String filename = entry.getKey(); + Model model = entry.getValue(); + ZipEntryFile zef = params.getEntry(filename); + if (zef instanceof ModelZipEntryFile) { + ModelZipEntryFile modelEntryFile = (ModelZipEntryFile) zef; + if (!modelEntryFile.isRoot() && !modelEntryFile.isMappingFile()) { + ModelSubmodelConnection submodel = new ModelSubmodelConnection(model, modelEntryFile.getType()); + submodel.setName(modelEntryFile.getName()); + result.addSubmodelConnection(submodel); + } + } + } + for (Layout layout : layouts) { + Layout topLayout = new Layout(layout); + result.addLayout(topLayout); + int modelId = 0; + topLayout.setDirectory(topLayout.getDirectory() + modelId); + modelId++; + for (ModelSubmodelConnection connection : result.getSubmodelConnections()) { + Layout childLayout = new Layout(layout); + // we need to set separate directory names for different submodels + childLayout.setDirectory(childLayout.getDirectory() + modelId); + modelId++; + childLayout.setParentLayout(topLayout); + connection.getSubmodel().addLayout(childLayout); + } + } + Model mappingModel = filenameModelMap.get(mapping); + if (mappingModel != null) { + for (Reaction reaction : mappingModel.getReactions()) { + processReaction(mapping, nameModelMap, result, reaction); + } + } + return result; + } catch (IOException e) { + throw new InvalidArgumentException(e); + } + } - /** - * Process a single reaction in mapping file (transfomring reaction into - * connection between submodels). - * - * @param mapping - * name of the mapping file - * @param nameModelMap - * mapping between file names and models - * @param topModel - * top model - * @param reaction - * reaction to transform into connection - */ - public void processReaction(String mapping, Map<String, Model> nameModelMap, Model topModel, Reaction reaction) { - if (reaction.getReactants().size() > 1) { - logger.warn("[SUBMODEL MAPPING] Reaction " + reaction.getIdReaction() + " in mapping file (" + mapping + ") contains too many reactants. Skipped"); - } else if (reaction.getProducts().size() > 1) { - logger.warn("[SUBMODEL MAPPING] Reaction " + reaction.getIdReaction() + " in mapping file (" + mapping + ") contains too many products. Skipped"); - } else if (reaction.getModifiers().size() > 0) { - logger.warn("[SUBMODEL MAPPING] Reaction " + reaction.getIdReaction() + " in mapping file (" + mapping + ") contains modifiers. Skipped"); - } else { - Element fromAlias = reaction.getReactants().get(0).getElement(); - Element toAlias = reaction.getProducts().get(0).getElement(); - if (!(fromAlias instanceof Species)) { - logger - .warn("[SUBMODEL MAPPING] Reaction " + reaction.getIdReaction() + " in mapping file (" + mapping + ") contains doesn't start in species. Skipped"); - } else if (!(toAlias instanceof Species)) { - logger.warn("[SUBMODEL MAPPING] Reaction " + reaction.getIdReaction() + " in mapping file (" + mapping + ") contains doesn't end in species. Skipped"); - } else { - Complex complexFrom = ((Species) fromAlias).getComplex(); - Complex complexTo = ((Species) toAlias).getComplex(); - if (complexFrom == null) { - logger.warn( - "[SUBMODEL MAPPING] Reaction " + reaction.getIdReaction() + " in mapping file (" + mapping + ") contains doesn't start inside complex. Skipped"); - } else if (complexTo == null && (!(toAlias instanceof Complex))) { - logger.warn( - "[SUBMODEL MAPPING] Reaction " + reaction.getIdReaction() + " in mapping file (" + mapping + ") contains doesn't end inside complex. Skipped"); - } else { - if (complexTo == null) { - complexTo = (Complex) toAlias; - } - String fromName = complexFrom.getName(); - String toName = complexTo.getName(); - Model fromModel = nameModelMap.get(fromName); - Model toModel = nameModelMap.get(toName); - if (fromModel == null) { - throw new InvalidArgumentException("Mapping file references to " + fromName + " submodel. But such model doesn't exist"); - } else if (toModel == null) { - throw new InvalidArgumentException("Mapping file references to " + toName + " submodel. But such model doesn't exist"); - } - Element source = fromModel.getElementByElementId(fromAlias.getName()); - if (source == null) { - throw new InvalidArgumentException("Mapping file references to element with alias: " + fromAlias.getName() + ". But such element doesn't exist"); - } - Element dest = null; - if (!(toAlias instanceof Complex)) { - dest = fromModel.getElementByElementId(toAlias.getName()); - } - SubmodelType type = SubmodelType.UNKNOWN; - if (fromAlias instanceof Protein) { - type = SubmodelType.DOWNSTREAM_TARGETS; - } else if (fromAlias instanceof Phenotype) { - type = SubmodelType.PATHWAY; - } - ElementSubmodelConnection connection = new ElementSubmodelConnection(toModel, type); - connection.setFromElement(source); - connection.setToElement(dest); - connection.setName(toModel.getName()); - source.setSubmodel(connection); - } - } - } - } + /** + * Process a single reaction in mapping file (transfomring reaction into + * connection between submodels). + * + * @param mapping + * name of the mapping file + * @param nameModelMap + * mapping between file names and models + * @param topModel + * top model + * @param reaction + * reaction to transform into connection + */ + public void processReaction(String mapping, Map<String, Model> nameModelMap, Model topModel, Reaction reaction) { + if (reaction.getReactants().size() > 1) { + logger.warn("[SUBMODEL MAPPING] Reaction " + reaction.getIdReaction() + " in mapping file (" + mapping + + ") contains too many reactants. Skipped"); + } else if (reaction.getProducts().size() > 1) { + logger.warn("[SUBMODEL MAPPING] Reaction " + reaction.getIdReaction() + " in mapping file (" + mapping + + ") contains too many products. Skipped"); + } else if (reaction.getModifiers().size() > 0) { + logger.warn("[SUBMODEL MAPPING] Reaction " + reaction.getIdReaction() + " in mapping file (" + mapping + + ") contains modifiers. Skipped"); + } else { + Element fromAlias = reaction.getReactants().get(0).getElement(); + Element toAlias = reaction.getProducts().get(0).getElement(); + if (!(fromAlias instanceof Species)) { + logger.warn("[SUBMODEL MAPPING] Reaction " + reaction.getIdReaction() + " in mapping file (" + mapping + + ") contains doesn't start in species. Skipped"); + } else if (!(toAlias instanceof Species)) { + logger.warn("[SUBMODEL MAPPING] Reaction " + reaction.getIdReaction() + " in mapping file (" + mapping + + ") contains doesn't end in species. Skipped"); + } else { + Complex complexFrom = ((Species) fromAlias).getComplex(); + Complex complexTo = ((Species) toAlias).getComplex(); + if (complexFrom == null) { + logger.warn("[SUBMODEL MAPPING] Reaction " + reaction.getIdReaction() + " in mapping file (" + mapping + + ") contains doesn't start inside complex. Skipped"); + } else if (complexTo == null && (!(toAlias instanceof Complex))) { + logger.warn("[SUBMODEL MAPPING] Reaction " + reaction.getIdReaction() + " in mapping file (" + mapping + + ") contains doesn't end inside complex. Skipped"); + } else { + if (complexTo == null) { + complexTo = (Complex) toAlias; + } + String fromName = complexFrom.getName(); + String toName = complexTo.getName(); + Model fromModel = nameModelMap.get(fromName); + Model toModel = nameModelMap.get(toName); + if (fromModel == null) { + throw new InvalidArgumentException( + "Mapping file references to " + fromName + " submodel. But such model doesn't exist"); + } else if (toModel == null) { + throw new InvalidArgumentException( + "Mapping file references to " + toName + " submodel. But such model doesn't exist"); + } + Element source = fromModel.getElementByElementId(fromAlias.getName()); + if (source == null) { + throw new InvalidArgumentException("Mapping file references to element with alias: " + fromAlias.getName() + + ". But such element doesn't exist"); + } + Element dest = null; + if (!(toAlias instanceof Complex)) { + dest = fromModel.getElementByElementId(toAlias.getName()); + } + SubmodelType type = SubmodelType.UNKNOWN; + if (fromAlias instanceof Protein) { + type = SubmodelType.DOWNSTREAM_TARGETS; + } else if (fromAlias instanceof Phenotype) { + type = SubmodelType.PATHWAY; + } + ElementSubmodelConnection connection = new ElementSubmodelConnection(toModel, type); + connection.setFromElement(source); + connection.setToElement(dest); + connection.setName(toModel.getName()); + source.setSubmodel(connection); + } + } + } + } - /** - * This method validates if information about model and submodels in the - * params are sufficient. If not then apropriate exception will be thrown. - * - * @param params - * parameters to validate - * @param zipFile - * original {@link ZipFile} - * @return name of the file containg mapping between submodels - */ - protected String validateSubmodelInformation(ComplexZipConverterParams params, ZipFile zipFile) { - Enumeration<? extends ZipEntry> entries = zipFile.entries(); - Set<String> processed = new HashSet<String>(); + /** + * This method validates if information about model and submodels in the params + * are sufficient. If not then appropriate exception will be thrown. + * + * @param params + * parameters to validate + * @param zipFile + * original {@link ZipFile} + * @return name of the file containing mapping between submodels + */ + protected String validateSubmodelInformation(ComplexZipConverterParams params, ZipFile zipFile) { + Enumeration<? extends ZipEntry> entries = zipFile.entries(); + Set<String> processed = new HashSet<>(); - String root = null; - String mapping = null; - while (entries.hasMoreElements()) { - ZipEntry entry = entries.nextElement(); - if (!entry.isDirectory()) { - String name = entry.getName(); - ZipEntryFile zef = params.getEntry(name); - if (zef == null) { - throw new InvalidArgumentException("No information found in params about file: " + name); - } - if (zef instanceof ModelZipEntryFile) { - ModelZipEntryFile modelEntryFile = (ModelZipEntryFile) zef; - if (modelEntryFile.isRoot()) { - if (root != null) { - throw new InvalidArgumentException("Two roots found: " + name + ", " + root + ". There can be only one."); - } - root = name; - } - if (modelEntryFile.isMappingFile()) { - if (mapping != null) { - throw new InvalidArgumentException("Two mapping files found: " + name + ", " + mapping + ". There can be only one."); - } - mapping = name; - } - } - processed.add(name); - } - } - if (root == null) { - throw new InvalidArgumentException("No root map found."); - } - for (String entryName : params.getFilenames()) { - if (!processed.contains(entryName)) { - throw new InvalidArgumentException("Entry " + entryName + " doesn't exists in the zip file."); - } - } - return mapping; - } + String root = null; + String mapping = null; + logger.debug(params.getFilenames()); + while (entries.hasMoreElements()) { + ZipEntry entry = entries.nextElement(); + if (!entry.isDirectory()) { + String name = entry.getName(); + ZipEntryFile zef = params.getEntry(name); + if (zef == null) { + throw new InvalidArgumentException("No information found in params about file: " + name); + } + if (zef instanceof ModelZipEntryFile) { + ModelZipEntryFile modelEntryFile = (ModelZipEntryFile) zef; + if (modelEntryFile.isRoot()) { + if (root != null) { + throw new InvalidArgumentException("Two roots found: " + name + ", " + root + ". There can be only one."); + } + root = name; + } + if (modelEntryFile.isMappingFile()) { + if (mapping != null) { + throw new InvalidArgumentException( + "Two mapping files found: " + name + ", " + mapping + ". There can be only one."); + } + mapping = name; + } + } + processed.add(name.toLowerCase()); + } + } + if (root == null) { + throw new InvalidArgumentException("No root map found."); + } + for (String entryName : params.getFilenames()) { + if (!processed.contains(entryName)) { + throw new InvalidArgumentException("Entry " + entryName + " doesn't exists in the zip file."); + } + } + return mapping; + } - /** - * Transforms {@link DataMiningZipEntryFile} into {@link DataMiningSet}. - * - * @param zipFile - * original {@link ZipFile} - * @param entry - * entry in a zip file - * @param dmEntry - * {@link DataMiningZipEntryFile} to transform - * @return {@link DataMiningSet} for a given {@link DataMiningZipEntryFile} - * @throws IOException - * thrown when there is a problem with accessing {@link ZipFile} - */ - protected DataMiningSet dataMiningZipEntryToDataMiningSet(ZipFile zipFile, ZipEntry entry, DataMiningZipEntryFile dmEntry) throws IOException { - DataMiningSet dmSet = new DataMiningSet(); - dmSet.setName(dmEntry.getName()); - dmSet.setInputData(IOUtils.toByteArray(zipFile.getInputStream(entry))); - dmSet.setDescription(dmEntry.getDescription()); - dmSet.setSource(dmEntry.getSource()); - dmSet.setType(dmEntry.getType()); - return dmSet; - } + /** + * Transforms {@link DataMiningZipEntryFile} into {@link DataMiningSet}. + * + * @param zipFile + * original {@link ZipFile} + * @param entry + * entry in a zip file + * @param dmEntry + * {@link DataMiningZipEntryFile} to transform + * @return {@link DataMiningSet} for a given {@link DataMiningZipEntryFile} + * @throws IOException + * thrown when there is a problem with accessing {@link ZipFile} + */ + protected DataMiningSet dataMiningZipEntryToDataMiningSet(ZipFile zipFile, ZipEntry entry, + DataMiningZipEntryFile dmEntry) throws IOException { + DataMiningSet dmSet = new DataMiningSet(); + dmSet.setName(dmEntry.getName()); + dmSet.setInputData(IOUtils.toByteArray(zipFile.getInputStream(entry))); + dmSet.setDescription(dmEntry.getDescription()); + dmSet.setSource(dmEntry.getSource()); + dmSet.setType(dmEntry.getType()); + return dmSet; + } - /** - * Transforms {@link LayoutZipEntryFile} into {@link Layout}. - * - * @param zipFile - * original {@link ZipFile} - * @param entry - * entry in a zip file - * @param params - * parameters used to make general conversion (we use directory where - * layout should be stored) - * @param layoutEntry - * {@link LayoutZipEntryFile} to transform - * @return {@link LAyout} for a given {@link LayoutZipEntryFile} - * @throws IOException - * thrown when there is a problem with accessing {@link ZipFile} - */ - protected Layout layoutZipEntryFileToLayout(ComplexZipConverterParams params, ZipFile zipFile, ZipEntry entry, LayoutZipEntryFile layoutEntry) - throws IOException { - Layout layout = new Layout(); - layout.setDescription(layoutEntry.getDescription()); - layout.setDirectory(params.getVisualizationDir() + "/" + layoutEntry.getName().replaceAll(" ", "_")); - UploadedFileEntry fileEntry = new UploadedFileEntry(); - fileEntry.setFileContent(IOUtils.toByteArray(zipFile.getInputStream(entry))); - fileEntry.setOriginalFileName(entry.getName()); - layout.setInputData(fileEntry); - layout.setPublicLayout(true); - layout.setTitle(layoutEntry.getName()); - return layout; - } + /** + * Transforms {@link LayoutZipEntryFile} into {@link Layout}. + * + * @param zipFile + * original {@link ZipFile} + * @param entry + * entry in a zip file + * @param params + * parameters used to make general conversion (we use directory where + * layout should be stored) + * @param layoutEntry + * {@link LayoutZipEntryFile} to transform + * @return {@link LAyout} for a given {@link LayoutZipEntryFile} + * @throws IOException + * thrown when there is a problem with accessing {@link ZipFile} + */ + protected Layout layoutZipEntryFileToLayout(ComplexZipConverterParams params, ZipFile zipFile, ZipEntry entry, + LayoutZipEntryFile layoutEntry) throws IOException { + Layout layout = new Layout(); + layout.setDescription(layoutEntry.getDescription()); + layout.setDirectory(params.getVisualizationDir() + "/" + layoutEntry.getName().replaceAll(" ", "_")); + UploadedFileEntry fileEntry = new UploadedFileEntry(); + fileEntry.setFileContent(IOUtils.toByteArray(zipFile.getInputStream(entry))); + fileEntry.setOriginalFileName(entry.getName()); + fileEntry.setLength(fileEntry.getFileContent().length); + layout.setInputData(fileEntry); + layout.setPublicLayout(true); + layout.setTitle(layoutEntry.getName()); + return layout; + } - /** - * Copy file from zip entry into temporary file. - * - * @param zipFile - * input zip file - * @param entry - * entry in the zip file - * @return {@link File} with a copy of data from zip entry - * @throws IOException - * thrown when there is a problem with input or output file - */ - protected File saveFileFromZipFile(ZipFile zipFile, ZipEntry entry) throws IOException { - InputStream is = zipFile.getInputStream(entry); - File result = File.createTempFile("temp-file-name", ".png"); - FileOutputStream fos = new FileOutputStream(result); - byte[] bytes = new byte[BUFFER_SIZE]; - int length; - while ((length = is.read(bytes)) >= 0) { - fos.write(bytes, 0, length); - } - fos.close(); - return result; - } + /** + * Copy file from zip entry into temporary file. + * + * @param zipFile + * input zip file + * @param entry + * entry in the zip file + * @return {@link File} with a copy of data from zip entry + * @throws IOException + * thrown when there is a problem with input or output file + */ + protected File saveFileFromZipFile(ZipFile zipFile, ZipEntry entry) throws IOException { + InputStream is = zipFile.getInputStream(entry); + File result = File.createTempFile("temp-file-name", ".png"); + FileOutputStream fos = new FileOutputStream(result); + byte[] bytes = new byte[BUFFER_SIZE]; + int length; + while ((length = is.read(bytes)) >= 0) { + fos.write(bytes, 0, length); + } + fos.close(); + return result; + } - /** - * Creates inctance of {@link IConverter} used as a template parameter for - * this class instatiation. - * - * @return inctance of {@link IConverter} - */ - protected IConverter createConverterInstance() { - IConverter converter; - try { - converter = converterClazz.newInstance(); - } catch (InstantiationException e) { - throw new InvalidClassException("Problem with instantation of the class: " + converterClazz, e); - } catch (IllegalAccessException e) { - throw new InvalidClassException("Problem with instantation of the class: " + converterClazz, e); - } - return converter; - } + /** + * Creates inctance of {@link IConverter} used as a template parameter for this + * class instatiation. + * + * @return inctance of {@link IConverter} + */ + protected IConverter createConverterInstance() { + IConverter converter; + try { + converter = converterClazz.newInstance(); + } catch (InstantiationException e) { + throw new InvalidClassException("Problem with instantation of the class: " + converterClazz, e); + } catch (IllegalAccessException e) { + throw new InvalidClassException("Problem with instantation of the class: " + converterClazz, e); + } + return converter; + } } diff --git a/converter/src/main/java/lcsb/mapviewer/converter/ComplexZipConverterParams.java b/converter/src/main/java/lcsb/mapviewer/converter/ComplexZipConverterParams.java index 2001d886ed..8d8daf907d 100644 --- a/converter/src/main/java/lcsb/mapviewer/converter/ComplexZipConverterParams.java +++ b/converter/src/main/java/lcsb/mapviewer/converter/ComplexZipConverterParams.java @@ -19,113 +19,112 @@ import lcsb.mapviewer.converter.zip.ZipEntryFile; */ public class ComplexZipConverterParams { - /** - * Default class logger. - */ - @SuppressWarnings("unused") - private final Logger logger = Logger.getLogger(ComplexZipConverterParams.class); - - /** - * Zip file where all maps are stored. - */ - private ZipFile zipFile; - - /** - * Mapping between filename and information provided by the user about the - * file. - */ - private Map<String, ZipEntryFile> entries = new HashMap<>(); - - /** - * Directory where additional visualization files should be stored. - */ - private String visualizationDir = null; - - /** - * Sets {@link #zipFile}. - * - * @param zipFile - * {@link #zipFile} - * @return object with all parameters - */ - public ComplexZipConverterParams zipFile(ZipFile zipFile) { - this.zipFile = zipFile; - return this; - } - - /** - * Sets {@link #zipFile}. - * - * @param filename - * name of the {@link #zipFile} - * @return object with all parameters - * @throws IOException - * throw when there is a problem with a file - */ - public ComplexZipConverterParams zipFile(String filename) throws IOException { - this.zipFile = new ZipFile(filename); - return this; - } - - /** - * Sets information about single entry in the zip file. - * - * @param entry - * set of information about single zip entry - * @return object with all parameters - */ - public ComplexZipConverterParams entry(ZipEntryFile entry) { - this.entries.put(entry.getFilename(), entry); - return this; - } - - /** - * @return the zipFile - * @see #zipFile - */ - public ZipFile getZipFile() { - return zipFile; - } - - /** - * Returns set of filenames put in this param object. - * - * @return set of filenames put in this param object - */ - public Set<String> getFilenames() { - return entries.keySet(); - } - - /** - * Sets {@link #visualizationDir} value. - * - * @param string - * new value - * @return instance of this class - */ - public ComplexZipConverterParams visualizationDir(String string) { - this.visualizationDir = string; - return this; - } - - /** - * Returns {@link #visualizationDir}. - * - * @return {@link #visualizationDir} - */ - public String getVisualizationDir() { - return visualizationDir; - } - - /** - * Return {@link ZipEntryFile entry} with information about single file in the - * zip archive identified by file name. - * - * @param name - * name of the file in zip archive - * @return {@link ZipEntryFile entry} with information about file - */ - public ZipEntryFile getEntry(String name) { - return entries.get(name); - } + /** + * Default class logger. + */ + @SuppressWarnings("unused") + private final Logger logger = Logger.getLogger(ComplexZipConverterParams.class); + + /** + * Zip file where all maps are stored. + */ + private ZipFile zipFile; + + /** + * Mapping between filename and information provided by the user about the file. + */ + private Map<String, ZipEntryFile> entries = new HashMap<>(); + + /** + * Directory where additional visualization files should be stored. + */ + private String visualizationDir = null; + + /** + * Sets {@link #zipFile}. + * + * @param zipFile + * {@link #zipFile} + * @return object with all parameters + */ + public ComplexZipConverterParams zipFile(ZipFile zipFile) { + this.zipFile = zipFile; + return this; + } + + /** + * Sets {@link #zipFile}. + * + * @param filename + * name of the {@link #zipFile} + * @return object with all parameters + * @throws IOException + * throw when there is a problem with a file + */ + public ComplexZipConverterParams zipFile(String filename) throws IOException { + this.zipFile = new ZipFile(filename); + return this; + } + + /** + * Sets information about single entry in the zip file. + * + * @param entry + * set of information about single zip entry + * @return object with all parameters + */ + public ComplexZipConverterParams entry(ZipEntryFile entry) { + this.entries.put(entry.getFilename().toLowerCase(), entry); + return this; + } + + /** + * @return the zipFile + * @see #zipFile + */ + public ZipFile getZipFile() { + return zipFile; + } + + /** + * Returns set of filenames put in this param object. + * + * @return set of filenames put in this param object + */ + public Set<String> getFilenames() { + return entries.keySet(); + } + + /** + * Sets {@link #visualizationDir} value. + * + * @param string + * new value + * @return instance of this class + */ + public ComplexZipConverterParams visualizationDir(String string) { + this.visualizationDir = string; + return this; + } + + /** + * Returns {@link #visualizationDir}. + * + * @return {@link #visualizationDir} + */ + public String getVisualizationDir() { + return visualizationDir; + } + + /** + * Return {@link ZipEntryFile entry} with information about single file in the + * zip archive identified by file name (file name is case insensitive). + * + * @param name + * name of the file in zip archive + * @return {@link ZipEntryFile entry} with information about file + */ + public ZipEntryFile getEntry(String name) { + return entries.get(name.toLowerCase()); + } } diff --git a/converter/src/test/java/lcsb/mapviewer/converter/AllTests.java b/converter/src/test/java/lcsb/mapviewer/converter/AllTests.java index f69bc17894..39feccf210 100644 --- a/converter/src/test/java/lcsb/mapviewer/converter/AllTests.java +++ b/converter/src/test/java/lcsb/mapviewer/converter/AllTests.java @@ -5,9 +5,10 @@ import org.junit.runners.Suite; import org.junit.runners.Suite.SuiteClasses; @RunWith(Suite.class) -@SuiteClasses({ ComplexZipConverterTest.class, // - OverviewParserTest.class, // - ProjectFactoryTest.class,// +@SuiteClasses({ ComplexZipConverterParamsTest.class, // + ComplexZipConverterTest.class, // + OverviewParserTest.class, // + ProjectFactoryTest.class,// }) public class AllTests { diff --git a/converter/src/test/java/lcsb/mapviewer/converter/ComplexZipConverterParamsTest.java b/converter/src/test/java/lcsb/mapviewer/converter/ComplexZipConverterParamsTest.java new file mode 100644 index 0000000000..c3ba1883c4 --- /dev/null +++ b/converter/src/test/java/lcsb/mapviewer/converter/ComplexZipConverterParamsTest.java @@ -0,0 +1,22 @@ +package lcsb.mapviewer.converter; + +import static org.junit.Assert.assertNotNull; + +import org.junit.Test; + +import lcsb.mapviewer.converter.zip.LayoutZipEntryFile; +import lcsb.mapviewer.converter.zip.ZipEntryFile; + +public class ComplexZipConverterParamsTest { + + @Test + public void testCaseInsensivity() { + ComplexZipConverterParams params = new ComplexZipConverterParams(); + ZipEntryFile entry = new LayoutZipEntryFile("TEST.xml", "test", null); + params.entry(entry); + assertNotNull(params.getEntry("TEST.xml")); + assertNotNull(params.getEntry("test.xml")); + assertNotNull(params.getEntry("TEST.XML")); + } + +} diff --git a/frontend-js/.gitignore b/frontend-js/.gitignore index 6e21c03544..163769681a 100644 --- a/frontend-js/.gitignore +++ b/frontend-js/.gitignore @@ -1,5 +1,7 @@ .idea/workspace.xml +.idea/jsLibraryMappings.xml /dist/ /coverage/ /node_modules/ /npm-debug.log + diff --git a/frontend-js/src/main/js/ServerConnector.js b/frontend-js/src/main/js/ServerConnector.js index 8f1db201c7..1024eb4590 100644 --- a/frontend-js/src/main/js/ServerConnector.js +++ b/frontend-js/src/main/js/ServerConnector.js @@ -73,7 +73,7 @@ ServerConnector.registerListenerType("onDataLoadStart"); ServerConnector.registerListenerType("onDataLoadStop"); ServerConnector.init(); -ServerConnector.getMinOverlayColorInt = function() { +ServerConnector.getMinOverlayColorInt = function () { var self = this; return self.getLoggedUser().then(function (user) { var userColor = user.getMinColor(); @@ -96,7 +96,7 @@ ServerConnector.returnUserOrSystemColor = function (userColor, systemPromisedCol ServerConnector.getSimpleOverlayColorInt = function () { var self = this; - return self.getLoggedUser().then(function(user) { + return self.getLoggedUser().then(function (user) { var userColor = user.getSimpleColor(); return self.returnUserOrSystemColor(userColor, self.getConfigurationParam(ConfigurationType.SIMPLE_COLOR_VAL)); }); @@ -152,10 +152,10 @@ ServerConnector._sendRequest = function (params) { return new Promise(function (resolve, reject) { request(params, function (error, response, body) { if (error) { - reject(new NetworkError(error.message, { - content: body, - url: url - })); + reject(new NetworkError(error.message, { + content: body, + url: url + })); } else if (response.statusCode !== 200) { reject(new NetworkError(params.url + " rejected with status code: " + response.statusCode, { content: body, @@ -229,8 +229,8 @@ ServerConnector.getApiBaseUrl = function () { ServerConnector.getServerBaseUrl = function () { if (this._serverBaseUrl === undefined) { var url = "" + window.location.href; - if (url.indexOf("?")>=0) { - url = url.substr(0,url.indexOf("?")); + if (url.indexOf("?") >= 0) { + url = url.substr(0, url.indexOf("?")); } if (!url.endsWith("/")) { url = url.substr(0, url.lastIndexOf("/") + 1); @@ -566,6 +566,30 @@ ServerConnector.getProjectSourceUrl = function (queryParams, filterParams) { }); }; + +ServerConnector.getFilesUrl = function () { + return this.getApiUrl({ + type: "files/" + }); +}; + +ServerConnector.getCreateFileUrl = function () { + return this.getApiUrl({ + url: this.getFilesUrl() + }); +}; +ServerConnector.getFileUrl = function (queryParams) { + return this.getApiUrl({ + url: this.getFilesUrl() + "/" + queryParams.id + }); +}; +ServerConnector.getUploadFileUrl = function (queryParams) { + return this.getApiUrl({ + url: this.getFileUrl(queryParams) + ":uploadContent" + }); +}; + + ServerConnector.getUsersUrl = function (queryParams, filterParams) { return this.getApiUrl({ type: "users/", @@ -619,7 +643,7 @@ ServerConnector.getModels = function (projectId) { var queryParams = {}; var filterParams = {}; var self = this; - return self.getProjectId(projectId).then(function(result) { + return self.getProjectId(projectId).then(function (result) { queryParams.projectId = result; return self.sendGetRequest(self.getModelsUrl(queryParams, filterParams)); }).then(function (content) { @@ -650,7 +674,7 @@ ServerConnector.getProject = function (projectId) { } project = self._projectsById[projectId]; return self.getModels(projectId); - }).then(function(models) { + }).then(function (models) { project.setModel(models[0]); return self.getLoggedUser(); }).then(function (user) { @@ -802,13 +826,13 @@ ServerConnector.getProjectStatistics = function (projectId) { var filterParams = {}; var self = this; var content; - return self.getProjectId(projectId).then(function(result) { + return self.getProjectId(projectId).then(function (result) { queryParams.projectId = result; return self.sendGetRequest(self.getProjectStatisticsUrl(queryParams, filterParams)); }).then(function (result) { content = JSON.parse(result); return self.getConfiguration(); - }).then(function(configuration) { + }).then(function (configuration) { return new ProjectStatistics(content, configuration); }); }; @@ -933,17 +957,17 @@ ServerConnector.getOverlays = function (params) { }); }; -ServerConnector.getOverlayElements = function(overlayId, projectId) { +ServerConnector.getOverlayElements = function (overlayId, projectId) { var self = this; if (overlayId === undefined) { throw new Error("Layout id must be defined"); } var queryParams = { - overlayId : overlayId, - modelId : "*", + overlayId: overlayId, + modelId: "*", }; var filterParams = {}; - return self.getProjectId(projectId).then(function(result) { + return self.getProjectId(projectId).then(function (result) { queryParams.projectId = result; return self.sendGetRequest(self.getOverlayElementsUrl(queryParams, filterParams)); }).then(function (content) { @@ -1041,7 +1065,7 @@ ServerConnector.getReactions = function (params) { columns: params.columns, participantId: params.participantId, }; - return self.getProjectId(params.projectId).then(function(result) { + return self.getProjectId(params.projectId).then(function (result) { queryParams.projectId = result; if (filterParams.id.length > 100 || filterParams.participantId.length > 100) { return self.sendPostRequest(self.getReactionsUrl(queryParams), filterParams); @@ -1059,10 +1083,10 @@ ServerConnector.getReactions = function (params) { }); }; -ServerConnector.getAliases = function(params) { +ServerConnector.getAliases = function (params) { var self = this; var queryParams = { - modelId : params.modelId, + modelId: params.modelId, }; if (params.ids === undefined) { params.ids = []; @@ -1081,14 +1105,14 @@ ServerConnector.getAliases = function(params) { includedCompartmentIds: params.includedCompartmentIds, }; - return self.getProjectId(params.projectId).then(function(result) { + return self.getProjectId(params.projectId).then(function (result) { queryParams.projectId = result; if (filterParams.id.length > 100) { return self.sendPostRequest(self.getAliasesUrl(queryParams), filterParams); } else { return self.sendGetRequest(self.getAliasesUrl(queryParams, filterParams)); } - }).then(function(content) { + }).then(function (content) { var array = JSON.parse(content); var result = []; for (var i = 0; i < array.length; i++) { @@ -1098,22 +1122,22 @@ ServerConnector.getAliases = function(params) { }); }; -ServerConnector.getLightComments = function(params) { - params.columns = [ "id", "elementId", "modelId", "type", "icon", "removed", "pinned" ]; +ServerConnector.getLightComments = function (params) { + params.columns = ["id", "elementId", "modelId", "type", "icon", "removed", "pinned"]; return this.getComments(params); }; -ServerConnector.getComments = function(params) { +ServerConnector.getComments = function (params) { var self = this; var queryParams = { - elementId : params.elementId, - elementType : params.elementType, - coordinates : params.coordinates, + elementId: params.elementId, + elementType: params.elementType, + coordinates: params.coordinates, }; var filterParams = { - columns : params.columns + columns: params.columns }; - return self.getProjectId(params.projectId).then(function(result) { + return self.getProjectId(params.projectId).then(function (result) { queryParams.projectId = result; return self.sendGetRequest(self.getCommentsUrl(queryParams, filterParams)); }).then(function (content) { @@ -1229,13 +1253,13 @@ ServerConnector.getDrugsByQuery = function (params) { }); }; -ServerConnector.getMiRnasByQuery = function(params) { +ServerConnector.getMiRnasByQuery = function (params) { var self = this; var queryParams = {}; var filterParams = { - query : params.query + query: params.query }; - return self.getProjectId(params.projectId).then(function(result) { + return self.getProjectId(params.projectId).then(function (result) { queryParams.projectId = result; return self.sendGetRequest(self.getSearchMiRnasUrl(queryParams, filterParams)); }).then(function (content) { @@ -1412,7 +1436,7 @@ ServerConnector.addComment = function (params) { elementId: params.elementId, elementType: params.elementType, coordinates: self.pointToString(params.coordinates), - modelId: params.modelId, + modelId: params.modelId }; var filterParams = params; delete filterParams.elementId; @@ -1443,7 +1467,7 @@ ServerConnector.addOverlay = function (params) { name: overlay.getName(), description: overlay.getDescription(), content: overlay.getContent(), - filename: overlay.getFilename(), + filename: overlay.getFilename() }; return self.getProjectId(params.projectId).then(function (result) { queryParams.projectId = result; @@ -1456,14 +1480,14 @@ ServerConnector.addOverlay = function (params) { ServerConnector.updateOverlay = function (overlay) { var self = this; var queryParams = { - overlayId: overlay.getId(), + overlayId: overlay.getId() }; var filterParams = { overlay: { name: overlay.getName(), description: overlay.getDescription(), creator: overlay.getCreator(), - publicOverlay: overlay.getPublicOverlay(), + publicOverlay: overlay.getPublicOverlay() } }; return self.sendPatchRequest(self.updateOverlayUrl(queryParams), filterParams); @@ -1524,9 +1548,9 @@ ServerConnector.getPublications = function (params) { length: params.length, sortColumn: params.sortColumn, sortOrder: params.sortOrder, - search: params.search, + search: params.search }; - return self.getProjectId(params.projectId).then(function(result) { + return self.getProjectId(params.projectId).then(function (result) { queryParams.projectId = result; return self.sendGetRequest(self.getPublicationsUrl(queryParams, filterParams)); }).then(function (content) { @@ -1534,7 +1558,7 @@ ServerConnector.getPublications = function (params) { }); }; -ServerConnector.getReferenceGenome = function(params) { +ServerConnector.getReferenceGenome = function (params) { var self = this; var filterParams = {}; return self.sendGetRequest(self.getReferenceGenomeUrl(params, filterParams)).then(function (content) { @@ -1542,4 +1566,34 @@ ServerConnector.getReferenceGenome = function(params) { }); }; +ServerConnector.uploadFile = function (params) { + var CHUNK_SIZE = 65535; + var self = this; + var data = new Uint8Array(params.content); + var filterParams = { + filename: params.filename, + length: data.length + }; + + return self.sendPostRequest(self.getCreateFileUrl(), filterParams).then(function (response) { + var uploadedLength = 0; + var createPromise = function (resultFileJson) { + var resultFile = JSON.parse(resultFileJson); + if (uploadedLength >= data.length) { + return Promise.resolve(resultFile); + } else { + var chunk = data.slice(uploadedLength, uploadedLength + CHUNK_SIZE); + logger.debug("SEND " + chunk.length + " bytes"); + var url = self.getUploadFileUrl({id: resultFile.id}); + return self.sendRequest({method: "POST", url: url, body: chunk}).then(function (resultFileJson) { + uploadedLength += chunk.length; + logger.debug("RESPONSE ", resultFileJson); + return createPromise(resultFileJson); + }) + } + }; + return createPromise(response); + }); +}; + module.exports = ServerConnector; diff --git a/frontend-js/src/main/js/gui/admin/AddProjectDialog.js b/frontend-js/src/main/js/gui/admin/AddProjectDialog.js index 966d914461..c6766dbec4 100644 --- a/frontend-js/src/main/js/gui/admin/AddProjectDialog.js +++ b/frontend-js/src/main/js/gui/admin/AddProjectDialog.js @@ -675,7 +675,7 @@ AddProjectDialog.prototype.processFile = function (file) { if (file) { return new Promise(function (resolve, reject) { var reader = new FileReader(); - reader.readAsText(file); + reader.readAsArrayBuffer(file); reader.onload = function (evt) { try { self.setFileContent(evt.target.result); @@ -694,7 +694,6 @@ AddProjectDialog.prototype.processFile = function (file) { }; AddProjectDialog.prototype.setFileContent = function (fileContent) { - logger.debug(fileContent); this._fileContent = fileContent; }; AddProjectDialog.prototype.getFileContent = function () { @@ -771,6 +770,7 @@ AddProjectDialog.prototype.setSemanticZooming = function (value) { AddProjectDialog.prototype.setFileParserForFilename = function (filename) { var self = this; + self._filename = filename; var select = $("[name='project-format']", self.getElement)[0]; var options = select.options; var optionId = 0; @@ -803,18 +803,20 @@ AddProjectDialog.prototype.getConverter = function () { AddProjectDialog.prototype.onSaveClicked = function () { var self = this; + var parserClass; return self.checkValidity().then(function () { return self.getConverter(); }).then(function (converter) { - var parserClass; if (converter !== null) { parserClass = converter.handler; } + return ServerConnector.uploadFile({filename: self._filename, content: self.getFileContent()}); + }).then(function (file) { var options = { "projectId": self.getProjectId(), "name": self.getName(), "parser": parserClass, - "file-content": self.getFileContent(), + "file-id": file.id, "auto-resize": self.isAutoMargin(), "cache": self.isCache(), "notify-email": self.getNotifyEmail(), @@ -978,4 +980,8 @@ AddProjectDialog.prototype.getEntryByFilename = function (filename) { return null; }; +AddProjectDialog.prototype.getFilename = function () { + return this._filename; +}; + module.exports = AddProjectDialog; diff --git a/frontend-js/src/test/js/ServerConnector-test.js b/frontend-js/src/test/js/ServerConnector-test.js index ab6fc4ec0a..0e756aae63 100644 --- a/frontend-js/src/test/js/ServerConnector-test.js +++ b/frontend-js/src/test/js/ServerConnector-test.js @@ -20,80 +20,80 @@ var logger = require('./logger'); var chai = require('chai'); var assert = chai.assert; -describe('ServerConnector', function() { - describe('getProject', function() { - it('default', function() { - return ServerConnector.getProject().then(function(result) { +describe('ServerConnector', function () { + describe('getProject', function () { + it('default', function () { + return ServerConnector.getProject().then(function (result) { assert.ok(result instanceof Project); assert.equal(result.getProjectId(), "sample"); assert.equal(logger.getWarnings().length, 0); }); }); - it('invalid project id', function() { - return ServerConnector.getProject("invalid_project_id").then(function(result) { + it('invalid project id', function () { + return ServerConnector.getProject("invalid_project_id").then(function (result) { assert.equal(result, null); }); }); - it('caching', function() { + it('caching', function () { var project; - return ServerConnector.getProject().then(function(result) { + return ServerConnector.getProject().then(function (result) { project = result; return ServerConnector.getProject(); - }).then(function(result) { + }).then(function (result) { assert.ok(result === project); }); }); }); - describe('updateProject', function() { - it('default', function() { + describe('updateProject', function () { + it('default', function () { var project; var newVersion = "2.01"; - return ServerConnector.getProject().then(function(result) { + return ServerConnector.getProject().then(function (result) { project = result; project.setVersion(newVersion); return ServerConnector.updateProject(project); - }).then(function(result) { + }).then(function (result) { assert.ok(project === result); assert.equal(newVersion, result.getVersion()); }); }); }); - describe('removeProject', function() { - it('default', function() { + describe('removeProject', function () { + it('default', function () { var project; - return ServerConnector.getProject().then(function(result) { + return ServerConnector.getProject().then(function (result) { project = result; return ServerConnector.removeProject(project.getProjectId()); - }).then(function(result) { + }).then(function (result) { assert.ok(project === result); }); }); }); - it('getModels', function() { - return ServerConnector.getModels("sample").then(function(models) { + it('getModels', function () { + return ServerConnector.getModels("sample").then(function (models) { assert.equal(1, models.length); assert.ok(models[0] instanceof MapModel); }); }); - it('getPublications', function() { - return ServerConnector.getPublications().then(function(result) { + it('getPublications', function () { + return ServerConnector.getPublications().then(function (result) { assert.equal(result.totalSize, 1); }); }); - it('getProjectId from GET params', function() { + it('getProjectId from GET params', function () { helper.setUrl("http://test/?id=test"); - return ServerConnector.getProjectId().then(function(result) { + return ServerConnector.getProjectId().then(function (result) { assert.equal(result, "test"); }); }); - it('getReactions with empty list of ids', function() { - return ServerConnector.getReactions([]).then(function(result) { + it('getReactions with empty list of ids', function () { + return ServerConnector.getReactions([]).then(function (result) { assert.equal(result.length, 2); var reaction = result[0]; assert.ok(reaction instanceof Reaction); @@ -102,8 +102,8 @@ describe('ServerConnector', function() { }); }); - it('getReactions without ids', function() { - return ServerConnector.getReactions([]).then(function(result) { + it('getReactions without ids', function () { + return ServerConnector.getReactions([]).then(function (result) { assert.equal(result.length, 2); var reaction = result[0]; assert.ok(reaction instanceof Reaction); @@ -112,8 +112,8 @@ describe('ServerConnector', function() { }); }); - it('getElements with empty list of ids', function() { - return ServerConnector.getAliases({}).then(function(result) { + it('getElements with empty list of ids', function () { + return ServerConnector.getAliases({}).then(function (result) { assert.equal(result.length, 30); var alias = result[0]; assert.ok(alias instanceof Alias); @@ -121,8 +121,8 @@ describe('ServerConnector', function() { }); }); - it('getOverlayElements', function() { - return ServerConnector.getOverlayElements(18077).then(function(result) { + it('getOverlayElements', function () { + return ServerConnector.getOverlayElements(18077).then(function (result) { assert.equal(result.length, 1); var layoutAlias = result[0]; assert.ok(layoutAlias instanceof LayoutAlias); @@ -132,80 +132,80 @@ describe('ServerConnector', function() { }); }); - it('idsToString', function() { - var ids = [ 3, 2, 9, 1, 6, 8, 3, 2, 9, 1, 7, 3 ]; + it('idsToString', function () { + var ids = [3, 2, 9, 1, 6, 8, 3, 2, 9, 1, 7, 3]; var str = ServerConnector.idsToString(ids); assert.equal(str, "1,2,3,6,7,8,9"); }); - it('getOverlaySourceDownloadUrl', function() { + it('getOverlaySourceDownloadUrl', function () { var id = 17296; return ServerConnector.getOverlaySourceDownloadUrl({ - overlayId : id - }).then(function(url) { + overlayId: id + }).then(function (url) { assert.ok(url); assert.ok(url.indexOf(id) >= 0); return ServerConnector.sendGetRequest(url); }); }); - it('getImageDownloadUrl', function() { + it('getImageDownloadUrl', function () { var modelId = 15781; return ServerConnector.getImageDownloadUrl({ - modelId : modelId, - handlerClass : "lcsb.mapviewer.converter.graphics.PngImageGenerator", - }).then(function(url) { + modelId: modelId, + handlerClass: "lcsb.mapviewer.converter.graphics.PngImageGenerator", + }).then(function (url) { assert.ok(url); assert.ok(url.indexOf(modelId) >= 0); return ServerConnector.sendGetRequest(url); }); }); - it('getModelDownloadUrl', function() { + it('getModelDownloadUrl', function () { var modelId = 15781; return ServerConnector.getModelDownloadUrl({ - modelId : modelId, - handlerClass : "lcsb.mapviewer.converter.model.celldesigner.CellDesignerXmlParser", - }).then(function(url) { + modelId: modelId, + handlerClass: "lcsb.mapviewer.converter.model.celldesigner.CellDesignerXmlParser", + }).then(function (url) { assert.ok(url); assert.ok(url.indexOf(modelId) >= 0); return ServerConnector.sendGetRequest(url); }); }); - it('getProjectSourceDownloadUrl', function() { - return ServerConnector.getProjectSourceDownloadUrl().then(function(url) { + it('getProjectSourceDownloadUrl', function () { + return ServerConnector.getProjectSourceDownloadUrl().then(function (url) { assert.ok(url); return ServerConnector.sendGetRequest(url); }); }); - it('addOverlay', function() { + it('addOverlay', function () { return ServerConnector.addOverlay({ - overlay : new LayoutData({ - name : "test nam", - description : "test desc", - content : "name color\nCAPN1 #00FF00\nPARK7 #AC0000", - filename : "test.txt" + overlay: new LayoutData({ + name: "test nam", + description: "test desc", + content: "name color\nCAPN1 #00FF00\nPARK7 #AC0000", + filename: "test.txt" }), - }).then(function(overlay) { + }).then(function (overlay) { assert.ok(overlay); }); }); - it('removeOverlay', function() { + it('removeOverlay', function () { return ServerConnector.removeOverlay({ - overlayId : 17296, + overlayId: 17296, }); }); - it('removeComment', function() { + it('removeComment', function () { return ServerConnector.removeComment({ - commentId : 4290, + commentId: 4290, }); }); - it('updateOverlay', function() { + it('updateOverlay', function () { var overlay = new LayoutData({}); overlay.setId(17296); overlay.setName("test nam2"); @@ -214,142 +214,153 @@ describe('ServerConnector', function() { return ServerConnector.updateOverlay(overlay); }); - it('logout', function() { - return ServerConnector.logout().then(function() { + it('logout', function () { + return ServerConnector.logout().then(function () { assert.equal(ServerConnector.getSessionData().getToken(), undefined); }); }); - it('getToken', function() { + it('getToken', function () { ServerConnector.getSessionData(null).setToken(undefined); - return ServerConnector.getToken().then(function(token) { + return ServerConnector.getToken().then(function (token) { assert.ok(token !== undefined); }); }); - it('getModelDownloadUrl', function() { + it('getModelDownloadUrl', function () { return ServerConnector.getModelDownloadUrl({ - backgroundOverlayId : "cv14081", - }).then(function(url) { + backgroundOverlayId: "cv14081", + }).then(function (url) { assert.ok(url); }); }); - it('getOverlayById', function() { - return ServerConnector.getOverlayById(18083, "complex_model_with_submaps").then(function(overlay) { + it('getOverlayById', function () { + return ServerConnector.getOverlayById(18083, "complex_model_with_submaps").then(function (overlay) { assert.ok(overlay); }); }); - it('getConfiguration', function() { - return ServerConnector.getConfiguration().then(function(configuration) { + it('getConfiguration', function () { + return ServerConnector.getConfiguration().then(function (configuration) { assert.ok(configuration instanceof Configuration); assert.ok(configuration.getElementTypes().length > 0); assert.ok(configuration.getReactionTypes().length > 0); }); }); - describe('getProjects', function() { - it('test caching', function() { + describe('getProjects', function () { + it('test caching', function () { var projects; - return ServerConnector.getProjects().then(function(result) { + return ServerConnector.getProjects().then(function (result) { projects = result; return ServerConnector.getProjects(); - }).then(function(result) { + }).then(function (result) { assert.ok(result === projects); }); }); - it('test force reload', function() { + it('test force reload', function () { var projects; var originalName; - return ServerConnector.getProjects().then(function(result) { + return ServerConnector.getProjects().then(function (result) { projects = result; originalName = projects[0].getName(); projects[0].setName("test name"); return ServerConnector.getProjects(true); - }).then(function(result) { + }).then(function (result) { assert.ok(result === projects); assert.equal(originalName, projects[0].getName()); }); }); }); - describe('login', function() { - it('try invalid credentials', function() { + describe('login', function () { + it('try invalid credentials', function () { var method = ServerConnector.sendPostRequest; - ServerConnector.sendPostRequest = function() { + ServerConnector.sendPostRequest = function () { return Promise.reject(new NetworkError("xxx", { - statusCode : HttpStatus.FORBIDDEN + statusCode: HttpStatus.FORBIDDEN })); }; - return ServerConnector.login("unknown", "unknown password").then(function() { + return ServerConnector.login("unknown", "unknown password").then(function () { ServerConnector.sendPostRequest = method; assert.ok(false); - }, function(error) { + }, function (error) { ServerConnector.sendPostRequest = method; assert.ok(error.message.indexOf("credentials") >= 0); }); }); }); - describe('getServerBaseUrl', function() { - it('url with GET arg that looks similar to original url', function() { - helper.setUrl("http://localhost:8080/minerva/login.xhtml?from=http://localhost:8080/minerva/?id=sample"); - var url = ServerConnector.getServerBaseUrl(); - assert.ok(url.indexOf("?")===-1); - }); + describe('getServerBaseUrl', function () { + it('url with GET arg that looks similar to original url', function () { + helper.setUrl("http://localhost:8080/minerva/login.xhtml?from=http://localhost:8080/minerva/?id=sample"); + var url = ServerConnector.getServerBaseUrl(); + assert.ok(url.indexOf("?") === -1); }); + }); - - describe('returnUserOrSystemColor ', function() { - it('user has empty color', function() { + describe('returnUserOrSystemColor ', function () { + it('user has empty color', function () { var systemColor = {}; - return ServerConnector.returnUserOrSystemColor("", Promise.resolve(systemColor)).then(function(result) { + return ServerConnector.returnUserOrSystemColor("", Promise.resolve(systemColor)).then(function (result) { assert.ok(result = systemColor); }); }); - it('user has defined color', function() { + it('user has defined color', function () { var userColor = {}; var systemColor = {}; - return ServerConnector.returnUserOrSystemColor(userColor, Promise.resolve(systemColor)).then(function(result) { + return ServerConnector.returnUserOrSystemColor(userColor, Promise.resolve(systemColor)).then(function (result) { assert.ok(result = userColor); }); }); }); - describe('readFile', function() { - it('check session expired', function() { + describe('readFile', function () { + it('check session expired', function () { ServerConnector.getSessionData().setToken(undefined); assert.ok(ServerConnector.getSessionData().getLogin()); - return ServerConnector.sendGetRequest("package.json", "Downloading projects").then(function() { + return ServerConnector.sendGetRequest("package.json", "Downloading projects").then(function () { assert.notOk(ServerConnector.getSessionData().getLogin()); }); }); }); - describe('getUsers', function() { - it('default', function() { - return ServerConnector.getUsers().then(function(users) { + describe('getUsers', function () { + it('default', function () { + return ServerConnector.getUsers().then(function (users) { assert.ok(users.length > 0); }); }); - it('refresh', function() { + it('refresh', function () { var user; var users; var modifiedName = "xxx name"; - return ServerConnector.getUsers().then(function(result) { + return ServerConnector.getUsers().then(function (result) { users = result; user = users[0]; user.setName(modifiedName); return ServerConnector.getUsers(true); - }).then(function(result) { + }).then(function (result) { assert.ok(users === result); assert.ok(user.getName() !== modifiedName); }); }); }); + describe('uploadFile', function () { + it('small file', function () { + return ServerConnector.uploadFile({ + filename: "test.txt", + content: new Uint8Array([1, 65, 90, 4, 8]) + }).then(function (file) { + logger.debug(file); + assert.ok(file.id); + }); + }); + }); + }); diff --git a/frontend-js/src/test/js/gui/admin/AddProjectDialog-test.js b/frontend-js/src/test/js/gui/admin/AddProjectDialog-test.js index 219804ccf8..3e1807a016 100644 --- a/frontend-js/src/test/js/gui/admin/AddProjectDialog-test.js +++ b/frontend-js/src/test/js/gui/admin/AddProjectDialog-test.js @@ -139,7 +139,7 @@ describe('AddProjectDialog', function () { }).then(function () { assert.ok(options["name"] !== undefined); assert.ok(options["projectId"] !== undefined); - assert.ok(options["file-content"] !== undefined); + assert.ok(options["file-id"] !== undefined); assert.ok(options["parser"] !== undefined); assert.ok(options["auto-resize"] !== undefined); assert.ok(options["cache"] !== undefined); @@ -148,6 +148,7 @@ describe('AddProjectDialog', function () { assert.ok(options["organism"] !== undefined); assert.ok(options["sbgn"] !== undefined); assert.ok(options["semantic-zoom"] !== undefined); + }).finally(function () { return dialog.destroy(); }); }); diff --git a/frontend-js/testFiles/apiCalls/files/6790.uploadContent/POST_token=MOCK_TOKEN_ID& b/frontend-js/testFiles/apiCalls/files/6790.uploadContent/POST_token=MOCK_TOKEN_ID& new file mode 100644 index 0000000000..b5a2599fb1 --- /dev/null +++ b/frontend-js/testFiles/apiCalls/files/6790.uploadContent/POST_token=MOCK_TOKEN_ID& @@ -0,0 +1,7 @@ +{ + "owner": "admin", + "filename": "test.txt", + "uploadedDataLength": 5, + "length": 5, + "id": 6790 +} \ No newline at end of file diff --git a/frontend-js/testFiles/apiCalls/files/6791.uploadContent/POST_token=MOCK_TOKEN_ID& b/frontend-js/testFiles/apiCalls/files/6791.uploadContent/POST_token=MOCK_TOKEN_ID& new file mode 100644 index 0000000000..78115f94a8 --- /dev/null +++ b/frontend-js/testFiles/apiCalls/files/6791.uploadContent/POST_token=MOCK_TOKEN_ID& @@ -0,0 +1,7 @@ +{ + "owner": "admin", + "filename": "test.xml", + "uploadedDataLength": 13, + "length": 13, + "id": 6791 +} \ No newline at end of file diff --git a/frontend-js/testFiles/apiCalls/files/POST_filename=test.txt&length=5&token=MOCK_TOKEN_ID& b/frontend-js/testFiles/apiCalls/files/POST_filename=test.txt&length=5&token=MOCK_TOKEN_ID& new file mode 100644 index 0000000000..bd4d1f74f1 --- /dev/null +++ b/frontend-js/testFiles/apiCalls/files/POST_filename=test.txt&length=5&token=MOCK_TOKEN_ID& @@ -0,0 +1,7 @@ +{ + "owner": "admin", + "filename": "test.txt", + "uploadedDataLength": 0, + "length": 5, + "id": 6790 +} \ No newline at end of file diff --git a/frontend-js/testFiles/apiCalls/files/POST_filename=test.xml&length=13&token=MOCK_TOKEN_ID& b/frontend-js/testFiles/apiCalls/files/POST_filename=test.xml&length=13&token=MOCK_TOKEN_ID& new file mode 100644 index 0000000000..232463c23b --- /dev/null +++ b/frontend-js/testFiles/apiCalls/files/POST_filename=test.xml&length=13&token=MOCK_TOKEN_ID& @@ -0,0 +1,7 @@ +{ + "owner": "admin", + "filename": "test.xml", + "uploadedDataLength": 0, + "length": 13, + "id": 6791 +} \ No newline at end of file diff --git a/model/src/main/java/lcsb/mapviewer/model/cache/UploadedFileEntry.java b/model/src/main/java/lcsb/mapviewer/model/cache/UploadedFileEntry.java index 0ef8844970..acecea373d 100644 --- a/model/src/main/java/lcsb/mapviewer/model/cache/UploadedFileEntry.java +++ b/model/src/main/java/lcsb/mapviewer/model/cache/UploadedFileEntry.java @@ -4,6 +4,10 @@ import java.io.Serializable; import javax.persistence.DiscriminatorValue; import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.ManyToOne; + +import lcsb.mapviewer.model.user.User; /** * Database object representing file uploaded into system. @@ -15,26 +19,50 @@ import javax.persistence.Entity; @DiscriminatorValue("UPLOADED_FILE_ENTRY") public class UploadedFileEntry extends FileEntry implements Serializable { - /** - * - */ - private static final long serialVersionUID = 1L; - - /** - * Default constructor. - */ - public UploadedFileEntry() { - - } - - /** - * Constructor that copies data from the parameter. - * - * @param original - * original object from which data will bbe copied - */ - public UploadedFileEntry(UploadedFileEntry original) { - super(original); - } + /** + * + */ + private static final long serialVersionUID = 1L; + + /** + * Length of the file. + */ + private long length; + + @ManyToOne(fetch = FetchType.LAZY) + private User owner; + + /** + * Default constructor. + */ + public UploadedFileEntry() { + + } + + /** + * Constructor that copies data from the parameter. + * + * @param original + * original object from which data will bbe copied + */ + public UploadedFileEntry(UploadedFileEntry original) { + super(original); + } + + public long getLength() { + return length; + } + + public void setLength(long length) { + this.length = length; + } + + public User getOwner() { + return owner; + } + + public void setOwner(User owner) { + this.owner = owner; + } } diff --git a/persist/src/db/11.1.0/fix_db_20171004.sql b/persist/src/db/11.1.0/fix_db_20171004.sql new file mode 100644 index 0000000000..bf80845a97 --- /dev/null +++ b/persist/src/db/11.1.0/fix_db_20171004.sql @@ -0,0 +1,12 @@ +--uploaded files that are uploaded in chunks should have info about how big should be the file +alter table file_entry add column length bigint; +update file_entry set length = octet_length(filecontent); + +--add foreign key +alter table file_entry add column owner_iddb integer; +ALTER TABLE file_entry ADD CONSTRAINT file_entry_owner_constraint FOREIGN KEY (owner_iddb) + REFERENCES public.user_table (iddb) MATCH SIMPLE + ON UPDATE NO ACTION ON DELETE NO ACTION; + +--add owners for layout files +update file_entry SET owner_iddb = (select (max(creator_iddb)) from layout where file_entry_iddb = file_entry.iddb); \ No newline at end of file diff --git a/rest-api/src/main/java/lcsb/mapviewer/api/BaseRestImpl.java b/rest-api/src/main/java/lcsb/mapviewer/api/BaseRestImpl.java index 9918dfa4cf..7729b2296f 100644 --- a/rest-api/src/main/java/lcsb/mapviewer/api/BaseRestImpl.java +++ b/rest-api/src/main/java/lcsb/mapviewer/api/BaseRestImpl.java @@ -38,263 +38,264 @@ import lcsb.mapviewer.services.view.AnnotationView; @Transactional(value = "txManager") public abstract class BaseRestImpl { - /** - * Default class logger. - */ - private Logger logger = Logger.getLogger(BaseRestImpl.class); - - @Autowired - private IModelService modelService; - - @Autowired - private IProjectService projectService; - - @Autowired - private IUserService userService; - - @Autowired - private MiriamConnector miriamConnector; - - @Autowired - private PubmedParser pubmedParser; - - @Autowired - private ElementMatcher elementMatcher; - - protected Map<String, Object> okStatus() { - Map<String, Object> result = new HashMap<>(); - return result; - } - - protected Map<String, Object> createMinifiedSearchResult(Object object) { - Map<String, Object> result = new HashMap<>(); - if (object instanceof Element) { - result.put("type", ElementIdentifierType.ALIAS); - Element element = (Element) object; - result.put("id", element.getId()); - result.put("modelId", element.getModel().getId()); - } else if (object instanceof Reaction) { - result.put("type", ElementIdentifierType.REACTION); - Reaction element = (Reaction) object; - result.put("id", element.getId()); - result.put("modelId", element.getModel().getId()); - - } else { - throw new InvalidStateException("Unknown type of result: " + object.getClass()); - } - return result; - }; - - protected Map<String, Object> createAnnotation(MiriamData annotation) { - if (annotation != null && annotation.getDataType() != null) { - Map<String, Object> result = new HashMap<>(); - if (annotation.getDataType().getUris().size() > 0) { - try { - result.put("link", miriamConnector.getUrlString(annotation)); - } catch (Exception e) { - logger.error("Problem with miriam: " + annotation, e); - } - } - if (MiriamType.PUBMED.equals(annotation.getDataType())) { - try { - Article article = pubmedParser.getPubmedArticleById(Integer.valueOf(annotation.getResource())); - result.put("article", article); - } catch (PubmedSearchException e) { - logger.error("Problem with accessing info about pubmed", e); - } - } - result.put("type", annotation.getDataType().name()); - result.put("resource", annotation.getResource()); - result.put("id", annotation.getId()); - return result; - } else { - throw new InvalidArgumentException("invalid miriam data: " + annotation); - } - }; - - protected Map<String, Object> createAnnotation(AnnotationView annotation) { - Map<String, Object> result = new HashMap<>(); - if (annotation != null && annotation.getType() != null) { - MiriamType type = MiriamType.getTypeByCommonName(annotation.getType()); - result.put("link", annotation.getLink()); - if (MiriamType.PUBMED.equals(type)) { - try { - Article article = pubmedParser.getPubmedArticleById(Integer.valueOf(annotation.getResource())); - result.put("article", article); - } catch (PubmedSearchException e) { - logger.error("Problem with accessing info about pubmed", e); - } - } - result.put("type", type); - result.put("resource", annotation.getResource()); - result.put("id", annotation.getIdObject()); - } - return result; - } - - protected Map<String, Object> createAnnotation(Article article) { - Map<String, Object> result = new HashMap<>(); - if (article != null) { - MiriamType type = MiriamType.PUBMED; - result.put("link", miriamConnector.getUrlString(new MiriamData(MiriamType.PUBMED, article.getId()))); - result.put("article", article); - result.put("type", type); - result.put("resource", article.getId()); - } - return result; - } - - protected List<Map<String, Object>> createAnnotations(Collection<?> references) { - List<Map<String, Object>> result = new ArrayList<>(); - for (Object miriamData : references) { - if (miriamData instanceof MiriamData) { - result.add(createAnnotation((MiriamData) miriamData)); - } else if (miriamData instanceof AnnotationView) { - result.add(createAnnotation((AnnotationView) miriamData)); - } else if (miriamData instanceof Article) { - result.add(createAnnotation((Article) miriamData)); - } else { - throw new InvalidArgumentException(); - } - } - return result; - } - - protected List<Model> getModels(String projectId, String modelId, String token) throws SecurityException { - Model model = modelService.getLastModelByProjectId(projectId, userService.getToken(token)); - List<Model> models = new ArrayList<>(); - - if (!modelId.equals("*")) { - for (String str : modelId.split(",")) { - models.add(model.getSubmodelById(Integer.valueOf(str))); - } - } else { - models.addAll(model.getSubmodels()); - models.add(model); - } - return models; - } - - /** - * @return the modelService - * @see #modelService - */ - public IModelService getModelService() { - return modelService; - } - - /** - * @param modelService - * the modelService to set - * @see #modelService - */ - public void setModelService(IModelService modelService) { - this.modelService = modelService; - } - - /** - * @return the userService - * @see #userService - */ - public IUserService getUserService() { - return userService; - } - - /** - * @param userService - * the userService to set - * @see #userService - */ - public void setUserService(IUserService userService) { - this.userService = userService; - } - - protected List<Map<String, Object>> prepareTargets(Collection<Target> targets, List<Model> models) { - List<Map<String, Object>> result = new ArrayList<>(); - for (Target target : targets) { - result.add(prepareTarget(target, models)); - } - result.sort(new Comparator<Map<String, Object>>() { - - @Override - public int compare(Map<String, Object> o1, Map<String, Object> o2) { - List<?> targetedObjects1 = (List<?>) o1.get("targetElements"); - List<?> targetedObjects2 = (List<?>) o2.get("targetElements"); - Integer size1 = 0; - Integer size2 = 0; - if (targetedObjects1 != null) { - size1 = targetedObjects1.size(); - } - if (targetedObjects2 != null) { - size2 = targetedObjects2.size(); - } - return -size1.compareTo(size2); - } - }); - return result; - } - - protected Map<String, Object> prepareTarget(Target target, List<Model> models) { - Map<String, Object> result = new HashMap<>(); - result.put("name", target.getName()); - result.put("references", createAnnotations(target.getReferences())); - result.put("targetParticipants", createAnnotations(target.getGenes())); - - List<Map<String, Object>> targetedObjects = new ArrayList<>(); - for (Model model : models) { - for (BioEntity object : model.getBioEntities()) { - if (elementMatcher.elementMatch(target, object)) { - Map<String, Object> elementMapping = new HashMap<>(); - - elementMapping.put("id", object.getId()); - elementMapping.put("type", getType(object)); - elementMapping.put("modelId", model.getId()); - targetedObjects.add(elementMapping); - } - } - } - - result.put("targetElements", targetedObjects); - - return result; - } - - private String getType(BioEntity object) { - if (object instanceof Reaction) { - return ElementIdentifierType.REACTION.getJsName(); - } else if (object instanceof Element) { - return ElementIdentifierType.ALIAS.getJsName(); - } else { - throw new InvalidArgumentException("Unknown type of element " + object.getClass()); - } - } - - /** - * @return the projectService - * @see #projectService - */ - public IProjectService getProjectService() { - return projectService; - } - - /** - * @param projectService the projectService to set - * @see #projectService - */ - public void setProjectService(IProjectService projectService) { - this.projectService = projectService; - } - - protected IConverter getModelParser(String handlerClass) throws QueryException { - IConverter parser; - if (SbgnmlXmlConverter.class.getCanonicalName().equals(handlerClass)) { - parser = new SbgnmlXmlConverter(); - } else if (CellDesignerXmlParser.class.getCanonicalName().equals(handlerClass)) { - parser = new CellDesignerXmlParser(); - } else { - throw new QueryException("Unknown handlerClass: " + handlerClass); - } - return parser; - } + /** + * Default class logger. + */ + private Logger logger = Logger.getLogger(BaseRestImpl.class); + + @Autowired + private IModelService modelService; + + @Autowired + private IProjectService projectService; + + @Autowired + private IUserService userService; + + @Autowired + private MiriamConnector miriamConnector; + + @Autowired + private PubmedParser pubmedParser; + + @Autowired + private ElementMatcher elementMatcher; + + protected Map<String, Object> okStatus() { + Map<String, Object> result = new HashMap<>(); + return result; + } + + protected Map<String, Object> createMinifiedSearchResult(Object object) { + Map<String, Object> result = new HashMap<>(); + if (object instanceof Element) { + result.put("type", ElementIdentifierType.ALIAS); + Element element = (Element) object; + result.put("id", element.getId()); + result.put("modelId", element.getModel().getId()); + } else if (object instanceof Reaction) { + result.put("type", ElementIdentifierType.REACTION); + Reaction element = (Reaction) object; + result.put("id", element.getId()); + result.put("modelId", element.getModel().getId()); + + } else { + throw new InvalidStateException("Unknown type of result: " + object.getClass()); + } + return result; + }; + + protected Map<String, Object> createAnnotation(MiriamData annotation) { + if (annotation != null && annotation.getDataType() != null) { + Map<String, Object> result = new HashMap<>(); + if (annotation.getDataType().getUris().size() > 0) { + try { + result.put("link", miriamConnector.getUrlString(annotation)); + } catch (Exception e) { + logger.error("Problem with miriam: " + annotation, e); + } + } + if (MiriamType.PUBMED.equals(annotation.getDataType())) { + try { + Article article = pubmedParser.getPubmedArticleById(Integer.valueOf(annotation.getResource())); + result.put("article", article); + } catch (PubmedSearchException e) { + logger.error("Problem with accessing info about pubmed", e); + } + } + result.put("type", annotation.getDataType().name()); + result.put("resource", annotation.getResource()); + result.put("id", annotation.getId()); + return result; + } else { + throw new InvalidArgumentException("invalid miriam data: " + annotation); + } + }; + + protected Map<String, Object> createAnnotation(AnnotationView annotation) { + Map<String, Object> result = new HashMap<>(); + if (annotation != null && annotation.getType() != null) { + MiriamType type = MiriamType.getTypeByCommonName(annotation.getType()); + result.put("link", annotation.getLink()); + if (MiriamType.PUBMED.equals(type)) { + try { + Article article = pubmedParser.getPubmedArticleById(Integer.valueOf(annotation.getResource())); + result.put("article", article); + } catch (PubmedSearchException e) { + logger.error("Problem with accessing info about pubmed", e); + } + } + result.put("type", type); + result.put("resource", annotation.getResource()); + result.put("id", annotation.getIdObject()); + } + return result; + } + + protected Map<String, Object> createAnnotation(Article article) { + Map<String, Object> result = new HashMap<>(); + if (article != null) { + MiriamType type = MiriamType.PUBMED; + result.put("link", miriamConnector.getUrlString(new MiriamData(MiriamType.PUBMED, article.getId()))); + result.put("article", article); + result.put("type", type); + result.put("resource", article.getId()); + } + return result; + } + + protected List<Map<String, Object>> createAnnotations(Collection<?> references) { + List<Map<String, Object>> result = new ArrayList<>(); + for (Object miriamData : references) { + if (miriamData instanceof MiriamData) { + result.add(createAnnotation((MiriamData) miriamData)); + } else if (miriamData instanceof AnnotationView) { + result.add(createAnnotation((AnnotationView) miriamData)); + } else if (miriamData instanceof Article) { + result.add(createAnnotation((Article) miriamData)); + } else { + throw new InvalidArgumentException(); + } + } + return result; + } + + protected List<Model> getModels(String projectId, String modelId, String token) throws SecurityException { + Model model = modelService.getLastModelByProjectId(projectId, userService.getToken(token)); + List<Model> models = new ArrayList<>(); + + if (!modelId.equals("*")) { + for (String str : modelId.split(",")) { + models.add(model.getSubmodelById(Integer.valueOf(str))); + } + } else { + models.addAll(model.getSubmodels()); + models.add(model); + } + return models; + } + + /** + * @return the modelService + * @see #modelService + */ + public IModelService getModelService() { + return modelService; + } + + /** + * @param modelService + * the modelService to set + * @see #modelService + */ + public void setModelService(IModelService modelService) { + this.modelService = modelService; + } + + /** + * @return the userService + * @see #userService + */ + public IUserService getUserService() { + return userService; + } + + /** + * @param userService + * the userService to set + * @see #userService + */ + public void setUserService(IUserService userService) { + this.userService = userService; + } + + protected List<Map<String, Object>> prepareTargets(Collection<Target> targets, List<Model> models) { + List<Map<String, Object>> result = new ArrayList<>(); + for (Target target : targets) { + result.add(prepareTarget(target, models)); + } + result.sort(new Comparator<Map<String, Object>>() { + + @Override + public int compare(Map<String, Object> o1, Map<String, Object> o2) { + List<?> targetedObjects1 = (List<?>) o1.get("targetElements"); + List<?> targetedObjects2 = (List<?>) o2.get("targetElements"); + Integer size1 = 0; + Integer size2 = 0; + if (targetedObjects1 != null) { + size1 = targetedObjects1.size(); + } + if (targetedObjects2 != null) { + size2 = targetedObjects2.size(); + } + return -size1.compareTo(size2); + } + }); + return result; + } + + protected Map<String, Object> prepareTarget(Target target, List<Model> models) { + Map<String, Object> result = new HashMap<>(); + result.put("name", target.getName()); + result.put("references", createAnnotations(target.getReferences())); + result.put("targetParticipants", createAnnotations(target.getGenes())); + + List<Map<String, Object>> targetedObjects = new ArrayList<>(); + for (Model model : models) { + for (BioEntity object : model.getBioEntities()) { + if (elementMatcher.elementMatch(target, object)) { + Map<String, Object> elementMapping = new HashMap<>(); + + elementMapping.put("id", object.getId()); + elementMapping.put("type", getType(object)); + elementMapping.put("modelId", model.getId()); + targetedObjects.add(elementMapping); + } + } + } + + result.put("targetElements", targetedObjects); + + return result; + } + + private String getType(BioEntity object) { + if (object instanceof Reaction) { + return ElementIdentifierType.REACTION.getJsName(); + } else if (object instanceof Element) { + return ElementIdentifierType.ALIAS.getJsName(); + } else { + throw new InvalidArgumentException("Unknown type of element " + object.getClass()); + } + } + + /** + * @return the projectService + * @see #projectService + */ + public IProjectService getProjectService() { + return projectService; + } + + /** + * @param projectService + * the projectService to set + * @see #projectService + */ + public void setProjectService(IProjectService projectService) { + this.projectService = projectService; + } + + protected IConverter getModelParser(String handlerClass) throws QueryException { + IConverter parser; + if (SbgnmlXmlConverter.class.getCanonicalName().equals(handlerClass)) { + parser = new SbgnmlXmlConverter(); + } else if (CellDesignerXmlParser.class.getCanonicalName().equals(handlerClass)) { + parser = new CellDesignerXmlParser(); + } else { + throw new QueryException("Unknown handlerClass: " + handlerClass); + } + return parser; + } } diff --git a/rest-api/src/main/java/lcsb/mapviewer/api/files/FileController.java b/rest-api/src/main/java/lcsb/mapviewer/api/files/FileController.java new file mode 100644 index 0000000000..ed2a03cd09 --- /dev/null +++ b/rest-api/src/main/java/lcsb/mapviewer/api/files/FileController.java @@ -0,0 +1,59 @@ +package lcsb.mapviewer.api.files; + +import java.util.Map; + +import org.apache.log4j.Logger; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.CookieValue; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import lcsb.mapviewer.api.BaseController; +import lcsb.mapviewer.api.ObjectNotFoundException; +import lcsb.mapviewer.common.Configuration; +import lcsb.mapviewer.services.SecurityException; + +@RestController +public class FileController extends BaseController { + /** + * Default class logger. + */ + @SuppressWarnings("unused") + private Logger logger = Logger.getLogger(FileController.class); + + @Autowired + private FileRestImpl fileRest; + + @RequestMapping(value = "/files/", method = { RequestMethod.POST }, produces = { MediaType.APPLICATION_JSON_VALUE }) + public Map<String, Object> createFile(// + @CookieValue(value = Configuration.AUTH_TOKEN) String token, // + @RequestParam(value = "filename") String filename, // + @RequestParam(value = "length") String length // + ) throws SecurityException { + return fileRest.createFile(token, filename, length); + } + + @RequestMapping(value = "/files/{id}", method = { RequestMethod.GET }, produces = { + MediaType.APPLICATION_JSON_VALUE }) + public Map<String, Object> getFile(// + @CookieValue(value = Configuration.AUTH_TOKEN) String token, // + @PathVariable(value = "id") String id // + ) throws SecurityException, ObjectNotFoundException { + return fileRest.getFile(token, id); + } + + @RequestMapping(value = "/files/{id}:uploadContent", method = { RequestMethod.POST }, produces = { + MediaType.APPLICATION_JSON_VALUE }) + public Map<String, Object> uploadContent(// + @CookieValue(value = Configuration.AUTH_TOKEN) String token, // + @PathVariable(value = "id") String id, // + @RequestBody byte[] data) throws SecurityException, ObjectNotFoundException { + return fileRest.uploadContent(token, id, data); + } + +} \ No newline at end of file diff --git a/rest-api/src/main/java/lcsb/mapviewer/api/files/FileRestImpl.java b/rest-api/src/main/java/lcsb/mapviewer/api/files/FileRestImpl.java new file mode 100644 index 0000000000..bb2ecfeae8 --- /dev/null +++ b/rest-api/src/main/java/lcsb/mapviewer/api/files/FileRestImpl.java @@ -0,0 +1,107 @@ +package lcsb.mapviewer.api.files; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.commons.lang3.ArrayUtils; +import org.hibernate.QueryException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; + +import lcsb.mapviewer.api.BaseRestImpl; +import lcsb.mapviewer.api.ObjectNotFoundException; +import lcsb.mapviewer.common.exception.InvalidStateException; +import lcsb.mapviewer.model.cache.UploadedFileEntry; +import lcsb.mapviewer.model.user.PrivilegeType; +import lcsb.mapviewer.model.user.User; +import lcsb.mapviewer.persist.dao.cache.UploadedFileEntryDao; +import lcsb.mapviewer.services.SecurityException; +import lcsb.mapviewer.services.interfaces.ILayoutService; + +@Transactional(value = "txManager") +public class FileRestImpl extends BaseRestImpl { + + @Autowired + private ILayoutService overlayService; + + @Autowired + private UploadedFileEntryDao uploadedFileEntryDao; + + public Map<String, Object> createFile(String token, String filename, String length) throws SecurityException { + User user = getUserService().getUserByToken(token); + if (!getUserService().userHasPrivilege(user, PrivilegeType.ADD_MAP) + && overlayService.getAvailableCustomLayoutsNumber(user) == 0) { + throw new SecurityException("Access denied"); + } + UploadedFileEntry entry = new UploadedFileEntry(); + entry.setOriginalFileName(filename); + entry.setFileContent(new byte[] {}); + entry.setLength(Long.valueOf(length)); + entry.setOwner(user); + uploadedFileEntryDao.add(entry); + try { + return getFile(token, entry.getId() + ""); + } catch (ObjectNotFoundException e) { + throw new InvalidStateException(e); + } + } + + public Map<String, Object> getFile(String token, String id) throws SecurityException, ObjectNotFoundException { + User user = getUserService().getUserByToken(token); + int fileId = Integer.valueOf(id); + UploadedFileEntry fileEntry = uploadedFileEntryDao.getById(fileId); + if (fileEntry == null) { + throw new ObjectNotFoundException("Object not found"); + } + if (fileEntry.getOwner() == null) { + throw new SecurityException("Access denied"); + } + if (!fileEntry.getOwner().getLogin().equals(user.getLogin())) { + throw new SecurityException("Access denied"); + } + return serializeEntry(fileEntry); + } + + private Map<String, Object> serializeEntry(UploadedFileEntry fileEntry) { + Map<String, Object> result = new HashMap<>(); + result.put("id", fileEntry.getId()); + result.put("filename", fileEntry.getOriginalFileName()); + result.put("length", fileEntry.getLength()); + result.put("owner", fileEntry.getOwner().getLogin()); + result.put("uploadedDataLength", fileEntry.getFileContent().length); + return result; + } + + public Map<String, Object> uploadContent(String token, String id, byte[] data) throws SecurityException, ObjectNotFoundException { + User user = getUserService().getUserByToken(token); + int fileId = Integer.valueOf(id); + UploadedFileEntry fileEntry = uploadedFileEntryDao.getById(fileId); + if (fileEntry == null) { + throw new ObjectNotFoundException("Object not found"); + } + if (fileEntry.getOwner() == null) { + throw new SecurityException("Access denied"); + } + if (!fileEntry.getOwner().getLogin().equals(user.getLogin())) { + throw new SecurityException("Access denied"); + } + long missingByteLength = fileEntry.getLength() - fileEntry.getFileContent().length; + if (data.length > missingByteLength) { + throw new QueryException( + "Too many bytes sent. There are " + missingByteLength + " missing bytes, but " + data.length + " sent."); + } + byte[] newConent = ArrayUtils.addAll(fileEntry.getFileContent(), data); + fileEntry.setFileContent(newConent); + uploadedFileEntryDao.update(fileEntry); + return serializeEntry(fileEntry); + } + + public UploadedFileEntryDao getUploadedFileEntryDao() { + return uploadedFileEntryDao; + } + + public void setUploadedFileEntryDao(UploadedFileEntryDao uploadedFileEntryDao) { + this.uploadedFileEntryDao = uploadedFileEntryDao; + } + +} diff --git a/rest-api/src/main/java/lcsb/mapviewer/api/projects/ProjectRestImpl.java b/rest-api/src/main/java/lcsb/mapviewer/api/projects/ProjectRestImpl.java index 6a50de4c1f..802493819f 100644 --- a/rest-api/src/main/java/lcsb/mapviewer/api/projects/ProjectRestImpl.java +++ b/rest-api/src/main/java/lcsb/mapviewer/api/projects/ProjectRestImpl.java @@ -7,7 +7,6 @@ import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; -import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; @@ -38,16 +37,15 @@ import lcsb.mapviewer.commands.SetFixedHierarchyLevelCommand; import lcsb.mapviewer.commands.SubModelCommand; import lcsb.mapviewer.common.Configuration; import lcsb.mapviewer.common.exception.InvalidArgumentException; -import lcsb.mapviewer.common.exception.NotImplementedException; import lcsb.mapviewer.converter.ConverterException; import lcsb.mapviewer.converter.IConverter; import lcsb.mapviewer.converter.graphics.AbstractImageGenerator.Params; +import lcsb.mapviewer.converter.graphics.DrawingException; +import lcsb.mapviewer.converter.graphics.ImageGenerators; import lcsb.mapviewer.converter.zip.ImageZipEntryFile; import lcsb.mapviewer.converter.zip.LayoutZipEntryFile; import lcsb.mapviewer.converter.zip.ModelZipEntryFile; import lcsb.mapviewer.converter.zip.ZipEntryFile; -import lcsb.mapviewer.converter.graphics.DrawingException; -import lcsb.mapviewer.converter.graphics.ImageGenerators; import lcsb.mapviewer.model.Project; import lcsb.mapviewer.model.cache.FileEntry; import lcsb.mapviewer.model.cache.UploadedFileEntry; @@ -66,6 +64,7 @@ import lcsb.mapviewer.model.map.reaction.Reaction; import lcsb.mapviewer.model.map.species.Element; import lcsb.mapviewer.model.user.PrivilegeType; import lcsb.mapviewer.model.user.User; +import lcsb.mapviewer.persist.dao.cache.UploadedFileEntryDao; import lcsb.mapviewer.services.SecurityException; import lcsb.mapviewer.services.interfaces.ILayoutService; import lcsb.mapviewer.services.interfaces.IProjectService; @@ -105,6 +104,9 @@ public class ProjectRestImpl extends BaseRestImpl { @Autowired private OverviewImageViewFactory factory; + @Autowired + private UploadedFileEntryDao uploadedFileEntryDao; + public ProjectMetaData getProject(String projectId, String token) throws SecurityException, ObjectNotFoundException { AuthenticationToken authenticationToken = getUserService().getToken(token); Project project = getProjectService().getProjectByProjectId(projectId, authenticationToken); @@ -294,9 +296,10 @@ public class ProjectRestImpl extends BaseRestImpl { imageGenerator.generate(handlerClass, params, file.getAbsolutePath()); - FileEntry entry = new UploadedFileEntry(); + UploadedFileEntry entry = new UploadedFileEntry(); entry.setOriginalFileName("map." + extension); entry.setFileContent(IOUtils.toByteArray(new FileInputStream(file))); + entry.setLength(entry.getFileContent().length); file.delete(); return entry; @@ -342,9 +345,10 @@ public class ProjectRestImpl extends BaseRestImpl { String fileExtension = parser.getFileExtension(); - FileEntry entry = new UploadedFileEntry(); + UploadedFileEntry entry = new UploadedFileEntry(); entry.setOriginalFileName("model." + fileExtension); entry.setFileContent(IOUtils.toByteArray(is)); + entry.setLength(entry.getFileContent().length); return entry; } @@ -490,9 +494,16 @@ public class ProjectRestImpl extends BaseRestImpl { } CreateProjectParams params = new CreateProjectParams(); String directory = path + "/../map_images/" + md5(projectId) + "/"; - String fileContent = getFirstValue(data.get("file-content")); - if (fileContent == null) { - throw new QueryException("file-content is obligatory"); + String fileId = getFirstValue(data.get("file-id")); + if (fileId == null) { + throw new QueryException("file-id is obligatory"); + } + UploadedFileEntry file = uploadedFileEntryDao.getById(Integer.valueOf(fileId)); + if (file == null) { + throw new QueryException("Invalid file id: " + fileId); + } + if (file.getOwner() == null || !file.getOwner().getLogin().equals(user.getLogin())) { + throw new SecurityException("Access denied to source file"); } String parserClass = getFirstValue(data.get("parser")); if (parserClass == null) { @@ -517,7 +528,7 @@ public class ProjectRestImpl extends BaseRestImpl { params.notifyEmail(getFirstValue(data.get("notify-email"))); params.projectDir(directory); params.projectDisease(getFirstValue(data.get("disease"))); - params.projectFile(new ByteArrayInputStream(fileContent.getBytes())); + params.projectFile(new ByteArrayInputStream(file.getFileContent())); params.projectId(projectId); params.projectName(getFirstValue(data.get("name"))); params.projectOrganism(getFirstValue(data.get("organism"))); @@ -545,6 +556,7 @@ public class ProjectRestImpl extends BaseRestImpl { ZipEntryFile entry = null; String entryType = (String) data.get("zip-entries[" + fileIndex + "][_type]").get(0); String filename = (String) data.get("zip-entries[" + fileIndex + "][_filename]").get(0); + logger.debug(filename); if ("MAP".equalsIgnoreCase(entryType)) { String submodelTypeKey = "zip-entries[" + fileIndex + "][_data][type][id]"; String rootKey = "zip-entries[" + fileIndex + "][_data][root]"; @@ -607,7 +619,7 @@ public class ProjectRestImpl extends BaseRestImpl { StringBuffer sb = new StringBuffer(); for (int i = 0; i < mdbytes.length; i++) { // CHECKSTYLE:OFF - // this magic formula transforms int into hex value + // this magic formula transforms integer into hex value sb.append(Integer.toString((mdbytes[i] & 0xff) + 0x100, 16).substring(1)); // CHECKSTYLE:ON } @@ -627,4 +639,12 @@ public class ProjectRestImpl extends BaseRestImpl { return getProject(projectId, token); } + public UploadedFileEntryDao getUploadedFileEntryDao() { + return uploadedFileEntryDao; + } + + public void setUploadedFileEntryDao(UploadedFileEntryDao uploadedFileEntryDao) { + this.uploadedFileEntryDao = uploadedFileEntryDao; + } + } diff --git a/rest-api/src/main/resources/applicationContext-rest.xml b/rest-api/src/main/resources/applicationContext-rest.xml index 2642267ee9..60c2553716 100644 --- a/rest-api/src/main/resources/applicationContext-rest.xml +++ b/rest-api/src/main/resources/applicationContext-rest.xml @@ -13,6 +13,8 @@ <bean id="ConfigurationRestImpl" class="lcsb.mapviewer.api.configuration.ConfigurationRestImpl"/> + <bean id="FileRestImpl" class="lcsb.mapviewer.api.files.FileRestImpl"/> + <bean id="CommentRestImpl" class="lcsb.mapviewer.api.projects.comments.CommentRestImpl"/> <bean id="ProjectRestImpl" class="lcsb.mapviewer.api.projects.ProjectRestImpl"/> <bean id="ModelRestImpl" class="lcsb.mapviewer.api.projects.models.ModelRestImpl"/> diff --git a/rest-api/src/test/java/lcsb/mapviewer/api/AllRestTests.java b/rest-api/src/test/java/lcsb/mapviewer/api/AllRestTests.java index ced165d503..f1f28f0d09 100644 --- a/rest-api/src/test/java/lcsb/mapviewer/api/AllRestTests.java +++ b/rest-api/src/test/java/lcsb/mapviewer/api/AllRestTests.java @@ -5,15 +5,17 @@ import org.junit.runners.Suite; import org.junit.runners.Suite.SuiteClasses; import lcsb.mapviewer.api.configuration.AllConfigurationTests; +import lcsb.mapviewer.api.files.AllFileTests; import lcsb.mapviewer.api.genomics.AllGenomicsTests; import lcsb.mapviewer.api.projects.AllProjectTests; import lcsb.mapviewer.api.users.AllUserTests; @RunWith(Suite.class) @SuiteClasses({ AllConfigurationTests.class, // - AllGenomicsTests.class, // - AllProjectTests.class, // - AllUserTests.class,// + AllFileTests.class, // + AllGenomicsTests.class, // + AllProjectTests.class, // + AllUserTests.class,// }) public class AllRestTests { diff --git a/rest-api/src/test/java/lcsb/mapviewer/api/RestTestFunctions.java b/rest-api/src/test/java/lcsb/mapviewer/api/RestTestFunctions.java index a1a84dbd5f..d6fec72043 100644 --- a/rest-api/src/test/java/lcsb/mapviewer/api/RestTestFunctions.java +++ b/rest-api/src/test/java/lcsb/mapviewer/api/RestTestFunctions.java @@ -63,253 +63,253 @@ import lcsb.mapviewer.services.view.AuthenticationToken; @Transactional(value = "txManager") @Rollback(true) -@ContextConfiguration( - locations = { "/applicationContext-persist.xml", "/applicationContext-annotation.xml", "/applicationContext-service.xml", "/applicationContext-rest.xml" }) +@ContextConfiguration(locations = { "/applicationContext-persist.xml", "/applicationContext-annotation.xml", + "/applicationContext-service.xml", "/applicationContext-rest.xml" }) @RunWith(SpringJUnit4ClassRunner.class) public abstract class RestTestFunctions { - private Logger logger = Logger.getLogger(RestTestFunctions.class); - - public double EPSILON = 1e-6; - - @Autowired - protected PasswordEncoder passwordEncoder; - - @Autowired - protected IUserService userService; - - @Autowired - protected DbUtils dbUtils; - - protected AuthenticationToken token; - - protected AuthenticationToken adminToken; - - @Before - public void generalSetUp() { - dbUtils.setAutoFlush(false); - - token = userService.login(Configuration.ANONYMOUS_LOGIN, null); - - // assume that we have admin account with all the privileges - adminToken = userService.login("admin", "admin"); - - } - - @After - public void generatTearDown() throws IOException { - File f = new File("map_images"); - if (f.exists()) { - logger.info("Removing output test directory: " + f.getAbsolutePath()); - FileUtils.deleteDirectory(f); - } - - } - - protected String readFile(String file) throws IOException { - StringBuilder stringBuilder = new StringBuilder(); - BufferedReader reader = new BufferedReader(new FileReader(file)); - try { - String line = null; - String ls = System.getProperty("line.separator"); - - while ((line = reader.readLine()) != null) { - stringBuilder.append(line); - stringBuilder.append(ls); - } - } finally { - reader.close(); - } - - return stringBuilder.toString(); - } - - protected Node getNodeFromXmlString(String text) throws InvalidXmlSchemaException { - InputSource is = new InputSource(); - is.setCharacterStream(new StringReader(text)); - return getXmlDocumentFromInputSource(is).getChildNodes().item(0); - } - - protected Document getXmlDocumentFromFile(String fileName) throws InvalidXmlSchemaException, IOException { - File file = new File(fileName); - InputStream inputStream = new FileInputStream(file); - Reader reader = null; - try { - reader = new InputStreamReader(inputStream, "UTF-8"); - InputSource is = new InputSource(reader); - - Document result = getXmlDocumentFromInputSource(is); - inputStream.close(); - return result; - } catch (UnsupportedEncodingException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - return null; - } - - protected Document getXmlDocumentFromInputSource(InputSource stream) throws InvalidXmlSchemaException { - DocumentBuilder db; - try { - db = DocumentBuilderFactory.newInstance().newDocumentBuilder(); - } catch (ParserConfigurationException e) { - throw new InvalidXmlSchemaException("Problem with xml parser"); - } - Document doc = null; - try { - doc = db.parse(stream); - } catch (SAXException e) { - logger.error(e); - } catch (IOException e) { - logger.error(e); - } - return doc; - } - - private static Map<String, Model> models = new HashMap<String, Model>(); - - protected Model getModelForFile(String fileName, boolean fromCache) throws Exception { - Model result = null; - if (!fromCache) { - logger.debug("File without cache: " + fileName); - result = getModelForFile(fileName); - } else { - result = RestTestFunctions.models.get(fileName); - if (result == null) { - logger.debug("File to cache: " + fileName); - - result = getModelForFile(fileName); - RestTestFunctions.models.put(fileName, result); - } - } - return result; - } - - private Model getModelForFile(String fileName) throws InvalidInputDataExecption, IOException { - if (fileName.endsWith("zip")) { - ComplexZipConverter parser = new ComplexZipConverter(CellDesignerXmlParser.class); - ComplexZipConverterParams complexParams; - complexParams = new ComplexZipConverterParams().zipFile(fileName); - ZipEntryFile entry1 = new ModelZipEntryFile("main.xml", "main", true, false, SubmodelType.UNKNOWN); - ZipEntryFile entry2 = new ModelZipEntryFile("submaps/s1.xml", "s1", false, false, SubmodelType.UNKNOWN); - ZipEntryFile entry3 = new ModelZipEntryFile("submaps/s2.xml", "s2", false, false, SubmodelType.UNKNOWN); - ZipEntryFile entry4 = new ModelZipEntryFile("submaps/s3.xml", "s3", false, false, SubmodelType.UNKNOWN); - ZipEntryFile entry5 = new ModelZipEntryFile("submaps/mapping.xml", "mapping", false, true, SubmodelType.UNKNOWN); - complexParams.entry(entry1); - complexParams.entry(entry2); - complexParams.entry(entry3); - complexParams.entry(entry4); - complexParams.entry(entry5); - - Model model =parser.createModel(complexParams); - model.setTileSize(256); - for (ModelSubmodelConnection connection: model.getSubmodelConnections()) { - connection.getSubmodel().setTileSize(256); - } - model.setProject(new Project()); - return model; - - } else { - Model model = new CellDesignerXmlParser().createModel(new ConverterParams().filename(fileName)); - model.setTileSize(256); - model.setProject(new Project()); - return model; - } - } - - protected String createTmpFileName() { - try { - File f = File.createTempFile("prefix", ".txt"); - String filename = f.getName(); - f.delete(); - return filename; - } catch (IOException e) { - e.printStackTrace(); - return null; - } - } - - protected String nodeToString(Node node) { - return nodeToString(node, false); - } - - protected String nodeToString(Node node, boolean includeHeadNode) { - if (node == null) - return null; - StringWriter sw = new StringWriter(); - try { - Transformer t = TransformerFactory.newInstance().newTransformer(); - t.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); - t.setOutputProperty(OutputKeys.INDENT, "yes"); - t.setOutputProperty(OutputKeys.METHOD, "xml"); - - NodeList list = node.getChildNodes(); - for (int i = 0; i < list.getLength(); i++) { - Node element = list.item(i); - t.transform(new DOMSource(element), new StreamResult(sw)); - } - } catch (TransformerException te) { - logger.debug("nodeToString Transformer Exception"); - } - if (includeHeadNode) { - return "<" + node.getNodeName() + ">" + sw.toString() + "</" + node.getNodeName() + ">"; - } - return sw.toString(); - } - - protected boolean equalFiles(String fileA, String fileB) throws IOException { - int BLOCK_SIZE = 65536; - FileInputStream inputStreamA = new FileInputStream(fileA); - FileInputStream inputStreamB = new FileInputStream(fileB); - // vary BLOCK_SIZE to suit yourself. - // it should probably a factor or multiple of the size of a disk - // sector/cluster. - // Note that your max heap size may need to be adjused - // if you have a very big block size or lots of these comparators. - - // assume inputStreamA and inputStreamB are streams from your two files. - byte[] streamABlock = new byte[BLOCK_SIZE]; - byte[] streamBBlock = new byte[BLOCK_SIZE]; - boolean match = true; - int bytesReadA = 0; - int bytesReadB = 0; - do { - bytesReadA = inputStreamA.read(streamABlock); - bytesReadB = inputStreamB.read(streamBBlock); - match = ((bytesReadA == bytesReadB) && Arrays.equals(streamABlock, streamBBlock)); - } while (match && (bytesReadA > -1)); - inputStreamA.close(); - inputStreamB.close(); - return match; - } - - public File createTempDirectory() throws IOException { - final File temp; - - temp = File.createTempFile("temp", Long.toString(System.nanoTime())); - - if (!(temp.delete())) { - throw new IOException("Could not delete temp file: " + temp.getAbsolutePath()); - } - - if (!(temp.mkdir())) { - throw new IOException("Could not create temp directory: " + temp.getAbsolutePath()); - } - - return (temp); - } - - protected String getWebpage(String accessUrl) throws IOException { - String inputLine; - StringBuilder tmp = new StringBuilder(); - URL url = new URL(accessUrl); - URLConnection urlConn = url.openConnection(); - BufferedReader in = new BufferedReader(new InputStreamReader(urlConn.getInputStream())); - - while ((inputLine = in.readLine()) != null) { - tmp.append(inputLine); - } - in.close(); - return tmp.toString(); - } + private Logger logger = Logger.getLogger(RestTestFunctions.class); + + public double EPSILON = 1e-6; + + @Autowired + protected PasswordEncoder passwordEncoder; + + @Autowired + protected IUserService userService; + + @Autowired + protected DbUtils dbUtils; + + protected AuthenticationToken token; + + protected AuthenticationToken adminToken; + + @Before + public void generalSetUp() { + dbUtils.setAutoFlush(false); + + token = userService.login(Configuration.ANONYMOUS_LOGIN, null); + + // assume that we have admin account with all the privileges + adminToken = userService.login("admin", "admin"); + + } + + @After + public void generatTearDown() throws IOException { + File f = new File("map_images"); + if (f.exists()) { + logger.info("Removing output test directory: " + f.getAbsolutePath()); + FileUtils.deleteDirectory(f); + } + + } + + protected String readFile(String file) throws IOException { + StringBuilder stringBuilder = new StringBuilder(); + BufferedReader reader = new BufferedReader(new FileReader(file)); + try { + String line = null; + String ls = System.getProperty("line.separator"); + + while ((line = reader.readLine()) != null) { + stringBuilder.append(line); + stringBuilder.append(ls); + } + } finally { + reader.close(); + } + + return stringBuilder.toString(); + } + + protected Node getNodeFromXmlString(String text) throws InvalidXmlSchemaException { + InputSource is = new InputSource(); + is.setCharacterStream(new StringReader(text)); + return getXmlDocumentFromInputSource(is).getChildNodes().item(0); + } + + protected Document getXmlDocumentFromFile(String fileName) throws InvalidXmlSchemaException, IOException { + File file = new File(fileName); + InputStream inputStream = new FileInputStream(file); + Reader reader = null; + try { + reader = new InputStreamReader(inputStream, "UTF-8"); + InputSource is = new InputSource(reader); + + Document result = getXmlDocumentFromInputSource(is); + inputStream.close(); + return result; + } catch (UnsupportedEncodingException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + return null; + } + + protected Document getXmlDocumentFromInputSource(InputSource stream) throws InvalidXmlSchemaException { + DocumentBuilder db; + try { + db = DocumentBuilderFactory.newInstance().newDocumentBuilder(); + } catch (ParserConfigurationException e) { + throw new InvalidXmlSchemaException("Problem with xml parser"); + } + Document doc = null; + try { + doc = db.parse(stream); + } catch (SAXException e) { + logger.error(e); + } catch (IOException e) { + logger.error(e); + } + return doc; + } + + private static Map<String, Model> models = new HashMap<String, Model>(); + + protected Model getModelForFile(String fileName, boolean fromCache) throws Exception { + Model result = null; + if (!fromCache) { + logger.debug("File without cache: " + fileName); + result = getModelForFile(fileName); + } else { + result = RestTestFunctions.models.get(fileName); + if (result == null) { + logger.debug("File to cache: " + fileName); + + result = getModelForFile(fileName); + RestTestFunctions.models.put(fileName, result); + } + } + return result; + } + + private Model getModelForFile(String fileName) throws InvalidInputDataExecption, IOException { + if (fileName.endsWith("zip")) { + ComplexZipConverter parser = new ComplexZipConverter(CellDesignerXmlParser.class); + ComplexZipConverterParams complexParams; + complexParams = new ComplexZipConverterParams().zipFile(fileName); + ZipEntryFile entry1 = new ModelZipEntryFile("main.xml", "main", true, false, SubmodelType.UNKNOWN); + ZipEntryFile entry2 = new ModelZipEntryFile("submaps/s1.xml", "s1", false, false, SubmodelType.UNKNOWN); + ZipEntryFile entry3 = new ModelZipEntryFile("submaps/s2.xml", "s2", false, false, SubmodelType.UNKNOWN); + ZipEntryFile entry4 = new ModelZipEntryFile("submaps/s3.xml", "s3", false, false, SubmodelType.UNKNOWN); + ZipEntryFile entry5 = new ModelZipEntryFile("submaps/mapping.xml", "mapping", false, true, SubmodelType.UNKNOWN); + complexParams.entry(entry1); + complexParams.entry(entry2); + complexParams.entry(entry3); + complexParams.entry(entry4); + complexParams.entry(entry5); + + Model model = parser.createModel(complexParams); + model.setTileSize(256); + for (ModelSubmodelConnection connection : model.getSubmodelConnections()) { + connection.getSubmodel().setTileSize(256); + } + model.setProject(new Project()); + return model; + + } else { + Model model = new CellDesignerXmlParser().createModel(new ConverterParams().filename(fileName)); + model.setTileSize(256); + model.setProject(new Project()); + return model; + } + } + + protected String createTmpFileName() { + try { + File f = File.createTempFile("prefix", ".txt"); + String filename = f.getName(); + f.delete(); + return filename; + } catch (IOException e) { + e.printStackTrace(); + return null; + } + } + + protected String nodeToString(Node node) { + return nodeToString(node, false); + } + + protected String nodeToString(Node node, boolean includeHeadNode) { + if (node == null) + return null; + StringWriter sw = new StringWriter(); + try { + Transformer t = TransformerFactory.newInstance().newTransformer(); + t.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); + t.setOutputProperty(OutputKeys.INDENT, "yes"); + t.setOutputProperty(OutputKeys.METHOD, "xml"); + + NodeList list = node.getChildNodes(); + for (int i = 0; i < list.getLength(); i++) { + Node element = list.item(i); + t.transform(new DOMSource(element), new StreamResult(sw)); + } + } catch (TransformerException te) { + logger.debug("nodeToString Transformer Exception"); + } + if (includeHeadNode) { + return "<" + node.getNodeName() + ">" + sw.toString() + "</" + node.getNodeName() + ">"; + } + return sw.toString(); + } + + protected boolean equalFiles(String fileA, String fileB) throws IOException { + int BLOCK_SIZE = 65536; + FileInputStream inputStreamA = new FileInputStream(fileA); + FileInputStream inputStreamB = new FileInputStream(fileB); + // vary BLOCK_SIZE to suit yourself. + // it should probably a factor or multiple of the size of a disk + // sector/cluster. + // Note that your max heap size may need to be adjused + // if you have a very big block size or lots of these comparators. + + // assume inputStreamA and inputStreamB are streams from your two files. + byte[] streamABlock = new byte[BLOCK_SIZE]; + byte[] streamBBlock = new byte[BLOCK_SIZE]; + boolean match = true; + int bytesReadA = 0; + int bytesReadB = 0; + do { + bytesReadA = inputStreamA.read(streamABlock); + bytesReadB = inputStreamB.read(streamBBlock); + match = ((bytesReadA == bytesReadB) && Arrays.equals(streamABlock, streamBBlock)); + } while (match && (bytesReadA > -1)); + inputStreamA.close(); + inputStreamB.close(); + return match; + } + + public File createTempDirectory() throws IOException { + final File temp; + + temp = File.createTempFile("temp", Long.toString(System.nanoTime())); + + if (!(temp.delete())) { + throw new IOException("Could not delete temp file: " + temp.getAbsolutePath()); + } + + if (!(temp.mkdir())) { + throw new IOException("Could not create temp directory: " + temp.getAbsolutePath()); + } + + return (temp); + } + + protected String getWebpage(String accessUrl) throws IOException { + String inputLine; + StringBuilder tmp = new StringBuilder(); + URL url = new URL(accessUrl); + URLConnection urlConn = url.openConnection(); + BufferedReader in = new BufferedReader(new InputStreamReader(urlConn.getInputStream())); + + while ((inputLine = in.readLine()) != null) { + tmp.append(inputLine); + } + in.close(); + return tmp.toString(); + } } diff --git a/rest-api/src/test/java/lcsb/mapviewer/api/files/AllFileTests.java b/rest-api/src/test/java/lcsb/mapviewer/api/files/AllFileTests.java new file mode 100644 index 0000000000..25e1d4be31 --- /dev/null +++ b/rest-api/src/test/java/lcsb/mapviewer/api/files/AllFileTests.java @@ -0,0 +1,11 @@ +package lcsb.mapviewer.api.files; + +import org.junit.runner.RunWith; +import org.junit.runners.Suite; +import org.junit.runners.Suite.SuiteClasses; + +@RunWith(Suite.class) +@SuiteClasses({ FileRestImplTest.class }) +public class AllFileTests { + +} diff --git a/rest-api/src/test/java/lcsb/mapviewer/api/files/FileRestImplTest.java b/rest-api/src/test/java/lcsb/mapviewer/api/files/FileRestImplTest.java new file mode 100644 index 0000000000..69ef990fdf --- /dev/null +++ b/rest-api/src/test/java/lcsb/mapviewer/api/files/FileRestImplTest.java @@ -0,0 +1,80 @@ +package lcsb.mapviewer.api.files; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import java.util.Map; + +import org.apache.commons.lang3.ArrayUtils; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; + +import lcsb.mapviewer.api.RestTestFunctions; +import lcsb.mapviewer.model.cache.UploadedFileEntry; +import lcsb.mapviewer.persist.dao.cache.UploadedFileEntryDao; + +public class FileRestImplTest extends RestTestFunctions { + + @Autowired + UploadedFileEntryDao uploadedFileEntryDao; + + @Autowired + FileRestImpl fileRestImpl; + + @Test(expected = lcsb.mapviewer.services.SecurityException.class) + public void testCreateFileWithoutPrivilege() throws Exception { + fileRestImpl.createFile(token.getId(), "test.txt", "100"); + } + + @Test + public void testCreateFile() throws Exception { + Map<String, Object> result = fileRestImpl.createFile(adminToken.getId(), "test.txt", "100"); + assertNotNull(result); + int id = (Integer) result.get("id"); + UploadedFileEntry file = uploadedFileEntryDao.getById(id); + assertNotNull(file); + assertEquals(100, file.getLength()); + assertEquals(0, file.getFileContent().length); + uploadedFileEntryDao.delete(file); + } + + @Test + public void testUploadContent() throws Exception { + byte[] dataChunk = new byte[] { 105, 103 }; + byte[] dataChunk2 = new byte[] { 32, 73 }; + byte[] dataChunkMerged = ArrayUtils.addAll(dataChunk, dataChunk2); + Map<String, Object> result = fileRestImpl.createFile(adminToken.getId(), "test.txt", "100"); + int id = (Integer) result.get("id"); + fileRestImpl.uploadContent(adminToken.getId(), id + "", dataChunk); + + UploadedFileEntry file = uploadedFileEntryDao.getById(id); + assertEquals(100, file.getLength()); + assertEquals(2, file.getFileContent().length); + assertArrayEquals(dataChunk, file.getFileContent()); + + fileRestImpl.uploadContent(adminToken.getId(), id + "", dataChunk2); + + assertEquals(100, file.getLength()); + assertEquals(4, file.getFileContent().length); + assertArrayEquals(dataChunkMerged, file.getFileContent()); + + uploadedFileEntryDao.delete(file); + } + + @Test + public void testUploadInvalidContent() throws Exception { + byte[] dataChunk = new byte[100]; + Map<String, Object> result = fileRestImpl.createFile(adminToken.getId(), "test.txt", "100"); + int id = (Integer) result.get("id"); + + try { + fileRestImpl.uploadContent(adminToken.getId(), id + "", dataChunk); + } finally { + uploadedFileEntryDao.getById(id); + UploadedFileEntry file = uploadedFileEntryDao.getById(id); + uploadedFileEntryDao.delete(file); + } + } + +} diff --git a/service/src/main/java/lcsb/mapviewer/services/SecurityException.java b/service/src/main/java/lcsb/mapviewer/services/SecurityException.java index dae25a9822..de076a2bbc 100644 --- a/service/src/main/java/lcsb/mapviewer/services/SecurityException.java +++ b/service/src/main/java/lcsb/mapviewer/services/SecurityException.java @@ -2,13 +2,17 @@ package lcsb.mapviewer.services; public class SecurityException extends Exception { - /** - * - */ - private static final long serialVersionUID = 1L; - - public SecurityException(String message) { - super(message); - } + /** + * + */ + private static final long serialVersionUID = 1L; + + public SecurityException(String message) { + super(message); + } + + public SecurityException() { + super(); + } } diff --git a/service/src/main/java/lcsb/mapviewer/services/impl/LayoutService.java b/service/src/main/java/lcsb/mapviewer/services/impl/LayoutService.java index 646931a612..91f445f061 100644 --- a/service/src/main/java/lcsb/mapviewer/services/impl/LayoutService.java +++ b/service/src/main/java/lcsb/mapviewer/services/impl/LayoutService.java @@ -351,6 +351,8 @@ public class LayoutService implements ILayoutService { UploadedFileEntry fileEntry = new UploadedFileEntry(); fileEntry.setFileContent(IOUtils.toByteArray(params.getColorInputStream())); fileEntry.setOriginalFileName(params.getLayoutFileName()); + fileEntry.setLength(fileEntry.getFileContent().length); + fileEntry.setOwner(params.getUser()); topLayout.setInputData(fileEntry); topLayout.setDescription(params.getDescription()); params.getModel().addLayout(topLayout); @@ -511,6 +513,8 @@ public class LayoutService implements ILayoutService { UploadedFileEntry fileEntry = new UploadedFileEntry(); fileEntry.setFileContent(IOUtils.toByteArray(params.getColorInputStream())); fileEntry.setOriginalFileName(params.getLayoutFileName()); + fileEntry.setLength(fileEntry.getFileContent().length); + fileEntry.setOwner(params.getUser()); topLayout.setInputData(fileEntry); topLayout.setDescription(params.getDescription()); params.getModel().addLayout(topLayout); diff --git a/service/src/main/java/lcsb/mapviewer/services/impl/ProjectService.java b/service/src/main/java/lcsb/mapviewer/services/impl/ProjectService.java index db91fe0556..f50002b773 100644 --- a/service/src/main/java/lcsb/mapviewer/services/impl/ProjectService.java +++ b/service/src/main/java/lcsb/mapviewer/services/impl/ProjectService.java @@ -119,1455 +119,1481 @@ import lcsb.mapviewer.services.view.ProjectViewFactory; @Transactional(value = "txManager") public class ProjectService implements IProjectService { - /** - * Size of the artifitial buffer that will be released when - * {@link OutOfMemoryError} is thrown to gain some free memory and report - * problem. - */ - private static final int OUT_OF_MEMORY_BACKUP_BUFFER_SIZE = 10000; - - /** - * How much time (out of 1.00) is used for creation of the data from file. - */ - private static final double CREATION_OF_DATA = 0.50; - - /** - * How much time (out of 1.00) is used for upploading of the data from file. - */ - private static final double UPLOAD_OF_DATA = 0.50; - - /** - * Default class logger. - */ - private Logger logger = Logger.getLogger(ProjectService.class); - - /** - * Data access object for projects. - */ - @Autowired - private ProjectDao projectDao; - - /** - * Data access object for models. - */ - @Autowired - private ModelDao modelDao; - - /** - * Data access object for users. - */ - @Autowired - private UserDao userDao; - - /** - * Service that allows to access and manage models. - */ - @Autowired - private IModelService modelService; - - /** - * Service that allows to access and manage comments. - */ - @Autowired - private ICommentService commentService; - - /** - * Service used to access logs. - * - * @see ILogService - */ - @Autowired - private ILogService logService; - - /** - * Service that manages and gives access to configuration parameters. - */ - @Autowired - private IConfigurationService configurationService; - - /** - * Services that manages and gives access to user information. - */ - @Autowired - private IUserService userService; - - /** - * Services that accessa data about chemicals. - */ - @Autowired - private IChemicalService chemicalService; - - /** - * Services that accessa data about drugs. - */ - @Autowired - private IDrugService drugService; - - /** - * Services that accessa data about mirna. - */ - @Autowired - private IMiRNAService mirnaService; - - /** - * Service that manages data mining information. - */ - @Autowired - private IDataMiningService dataMiningService; - - /** - * Data access object for comments. - */ - @Autowired - private CommentDao commentDao; - - /** - * Service that provides password encoding. - */ - @Autowired - private PasswordEncoder passwordEncoder; - - /** - * Module that allows to annotate maps. - */ - @Autowired - private ModelAnnotator modelAnnotator; - - /** - * Utils that help to manage the sessions in custom multithreaded - * implementation. - */ - @Autowired - private DbUtils dbUtils; - - /** - * Factory object for {@link ProjectView} elements. - */ - @Autowired - private ProjectViewFactory projectViewFactory; - - /** - * Access point and parser for the online ctd database. - */ - @Autowired - private MeSHParser meshParser; - - /** - * Access point and parser for the online ctd database. - */ - @Autowired - private TaxonomyBackend taxonomyBackend; - - /** - * Class that helps to generate images for google maps API. - */ - private MapGenerator generator = new MapGenerator(); - - @Override - public Project getProjectByProjectId(String name, AuthenticationToken token) throws UserAccessException { - Project result = projectDao.getProjectByProjectId(name); - if (result == null) { - return result; - } - if (userService.userHasPrivilege(token, PrivilegeType.VIEW_PROJECT, result)) { - return result; - } else if (userService.userHasPrivilege(token, PrivilegeType.ADD_MAP)) { - return result; - } - throw new UserAccessException("User cannot access project"); - } - - @Override - public boolean projectExists(String projectName) { - if (projectName == null || projectName.equals("")) { - return false; - } - return projectDao.isProjectExistsByName(projectName); - } - - @Override - public List<Project> getAllProjects(AuthenticationToken token) { - List<Project> projects = projectDao.getAll(); - if (userService.userHasPrivilege(token, PrivilegeType.ADD_MAP)) { - return projects; - } - List<Project> result = new ArrayList<>(); - for (Project project : projects) { - if (userService.userHasPrivilege(token, PrivilegeType.VIEW_PROJECT, project)) { - result.add(project); - } - } - return result; - } - - @Override - public List<ProjectView> getAllProjectViews(AuthenticationToken token) { - List<ProjectView> result = new ArrayList<>(); - List<Project> projects = getAllProjects(token); - for (Project project : projects) { - result.add(projectViewFactory.create(project)); - } - return result; - } - - @Override - public void updateProject(ProjectView selectedProject) { - Project project = projectDao.getById(selectedProject.getIdObject()); - project.setProjectId(selectedProject.getProjectId()); - project.setName(selectedProject.getProjectName()); - project.setVersion(selectedProject.getVersion()); - project.setNotifyEmail(selectedProject.getNotifyEmail()); - MiriamData disease = null; - MiriamData organism = null; - if (selectedProject.getNewDiseaseName() != null && !selectedProject.getNewDiseaseName().isEmpty()) { - disease = new MiriamData(MiriamType.MESH_2012, selectedProject.getNewDiseaseName()); - } - if (selectedProject.getNewOrganismName() != null && !selectedProject.getNewOrganismName().isEmpty()) { - organism = new MiriamData(MiriamType.TAXONOMY, selectedProject.getNewOrganismName()); - } - try { - if (meshParser.isValidMeshId(disease)) { - project.setDisease(disease); - } else { - project.setDisease(null); - selectedProject.setNewDiseaseName(null); - } - } catch (AnnotatorException e) { - logger.error("Problem with accessing mesh", e); - project.setDisease(null); - selectedProject.setNewDiseaseName(null); - } - try { - if (taxonomyBackend.getNameForTaxonomy(organism) != null) { - project.setOrganism(organism); - } else { - project.setOrganism(null); - selectedProject.setNewOrganismName(null); - } - } catch (TaxonomySearchException e) { - logger.error("Problem with accessing taxonomy db", e); - project.setOrganism(null); - selectedProject.setNewOrganismName(null); - } - for (ModelData model : project.getModels()) { - if (model.getId().equals(selectedProject.getModelId())) { - modelService.updateModel(model, selectedProject); - } - } - projectDao.update(project); - } - - @Override - public void removeProject(ProjectView selectedProject, String homeDir, boolean async, AuthenticationToken token) throws UserAccessException { - Project project = projectDao.getById(selectedProject.getIdObject()); - removeProject(project, homeDir, async, token); - } - - /** - * Removes all types of privileges from every user to the project given in the - * parameter. - * - * @param project - * from which project we remove privileges - */ - private void removePrivilegesForProject(Project project) { - for (PrivilegeType type : PrivilegeType.values()) { - if (type.getPrivilegeObjectType() == Project.class) { - userService.dropPrivilegesForObjectType(type, project.getId()); - } - } - } - - @Override - public ProjectView getProjectViewByProjectId(String name, AuthenticationToken token) throws UserAccessException { - Project project = getProjectByProjectId(name, token); - return projectViewFactory.create(project); - } - - @Override - public void removeProject(final Project p, final String dir, final boolean async, AuthenticationToken token) throws UserAccessException { - if (!userService.userHasPrivilege(userService.getUserByToken(token), PrivilegeType.PROJECT_MANAGEMENT)) { - throw new UserAccessException("User cannot remove project"); - } - - final String homeDir; - if (dir != null) { - if (p.getDirectory() != null) { - homeDir = dir + "/../map_images/" + p.getDirectory() + "/"; - } else { - homeDir = dir + "/../map_images/"; - } - } else { - if (p.getDirectory() != null) { - homeDir = p.getDirectory() + "/"; - } else { - homeDir = null; - } - } - removePrivilegesForProject(p); - updateProjectStatus(p, ProjectStatus.REMOVING, 0, new CreateProjectParams()); - Thread computations = new Thread(new Runnable() { - - @Override - public void run() { - if (async) { - // because we are running this in separate thread we need to open a - // new - // session for db connection - dbUtils.createSessionForCurrentThread(); - } - - Project project = projectDao.getById(p.getId()); - - try { - String email = null; - MapGenerator mapGenerator = new MapGenerator(); - for (ModelData originalModel : project.getModels()) { - List<ModelData> models = new ArrayList<ModelData>(); - models.add(originalModel); - for (ModelSubmodelConnection connection : originalModel.getSubmodels()) { - models.add(connection.getSubmodel()); - } - modelService.removeModelFromCache(originalModel); - for (ModelData model : models) { - logger.debug("Remove model: " + model.getId()); - commentService.removeCommentsForModel(model); - - for (Layout layout : model.getLayouts()) { - try { - mapGenerator.removeLayout(layout, homeDir); - } catch (IOException e) { - logger.error("Problem with removing directory: " + layout.getDirectory(), e); - } - } - } - email = project.getNotifyEmail(); - } - projectDao.delete(project); - if (async) { - projectDao.commit(); - } - - LogParams params = new LogParams().type(LogType.MAP_REMOVED).object(project); - logService.log(params); - - if (email != null) { - try { - sendSuccesfullRemoveEmail(project.getProjectId(), email); - } catch (MessagingException e) { - logger.error("Problem with sending remove email.", e); - } - } - modelService.removeModelFromCacheByProjectId(p.getProjectId()); - - } catch (HibernateException e) { - logger.error("Problem with database", e); - handleHibernateExceptionRemovingReporting(project, e, token); - } finally { - if (async) { - // close the transaction for this thread - dbUtils.closeSessionForCurrentThread(); - } - } - if (homeDir != null) { - File homeDirFile = new File(homeDir); - if (homeDirFile.exists()) { - logger.debug("Removing project directory: " + homeDirFile.getAbsolutePath()); - try { - FileUtils.deleteDirectory(homeDirFile); - } catch (IOException e) { - logger.error("Problem with removing diriectory", e); - } - } - } - - } - }); - - if (async) { - computations.start(); - } else { - computations.run(); - } - - } - - /** - * When we encountered hibernate exception we need to handle error reporting - * differently (hibernate session is broken). This method handles such case - * when hibernate excedption occured when removing project. - * - * @param originalProject - * project that was beining removed - * @param exception - * hibernate exception that caused problems - */ - protected void handleHibernateExceptionRemovingReporting(Project originalProject, HibernateException exception, AuthenticationToken token) { - // we need to open separate thread because current one thrown db exception - // and transaction is corrupetd and will be rolledback - Thread reportInSeparateThread = new Thread(new Runnable() { - - @Override - public void run() { - dbUtils.createSessionForCurrentThread(); - try { - // we need to get the project from db, because session where - // originalProject was retrieved is broken - Project project = getProjectByProjectId(originalProject.getProjectId(), token); - String errorMessage = "Severe problem with removing object. Underlaying eror:\n" + exception.getMessage() - + "\nMore information can be found in log file."; - project.setErrors(errorMessage + "\n" + project.getErrors()); - project.setStatus(ProjectStatus.FAIL); - projectDao.update(project); - } catch (UserAccessException e) { - logger.error(e, e); - } finally { - dbUtils.closeSessionForCurrentThread(); - } - } - - }); - reportInSeparateThread.start(); - } - - @Override - public void addProject(Project project) { - projectDao.add(project); - - } - - @Override - public ProjectView getProjectViewById(Integer id, AuthenticationToken token) throws UserAccessException { - Project project = projectDao.getById(id); - if (userService.userHasPrivilege(token, PrivilegeType.VIEW_PROJECT, project)) { - return projectViewFactory.create(project); - } else if (userService.userHasPrivilege(token, PrivilegeType.ADD_MAP)) { - return projectViewFactory.create(project); - } else { - throw new UserAccessException("User cannot access project"); - } - } - - /** - * This methods add privileges for the users listed in params to the project. - * - * @param project - * to which project we add privileges - * @param params - * which users should be included to have privileges to the project - */ - private void addUsers(Project project, CreateProjectParams params) { - if (project == null) { - logger.warn("Users won't be added. Project not defined"); - return; - } - List<String[]> users = params.getUsers(); - String[][] newUsers = new String[users.size() + 1][2]; - for (int i = 0; i < users.size(); i++) { - for (int j = 0; j < 2; j++) { - newUsers[i][j] = users.get(i)[j]; - } - } - newUsers[users.size()][0] = "anonymous"; - newUsers[users.size()][1] = ""; - for (int i = 0; i < newUsers.length; i++) { - boolean admin = (users.size() != i); - String login = newUsers[i][0]; - String passwd = newUsers[i][1]; - User user = userService.getUserByLogin(login); - if (userService.getUserByLogin(login) == null) { - logger.debug("User " + login + " does not exist. Creating"); - user = new User(); - user.setCryptedPassword(passwordEncoder.encode(passwd)); - user.setLogin(login); - userService.addUser(user); - } - if (project != null) { - logger.debug("Privileges for " + login + " for project " + project.getProjectId()); - ObjectPrivilege privilege = new ObjectPrivilege(project, 1, PrivilegeType.VIEW_PROJECT, user); - userService.setUserPrivilege(user, privilege); - if (admin) { - privilege = new ObjectPrivilege(project, 1, PrivilegeType.LAYOUT_MANAGEMENT, user); - userService.setUserPrivilege(user, privilege); - privilege = new ObjectPrivilege(project, 1, PrivilegeType.EDIT_COMMENTS_PROJECT, user); - userService.setUserPrivilege(user, privilege); - } - - } - } - } - - /** - * This method creates set of images for the model layouts. - * - * @param originalModel - * model for which we create layout images - * @param params - * configuration parameters including set of layouts to generate - * @throws IOException - * thrown when there are problems with generating files - * @throws DrawingException - * thrown when there was a problem with drawing a map - * @throws CommandExecutionException - * thrown when one of the files describing layouts is invalid - */ - protected void createImages(final Model originalModel, final CreateProjectParams params) throws IOException, DrawingException, CommandExecutionException { - if (!params.isImages()) { - return; - } - updateProjectStatus(originalModel.getProject(), ProjectStatus.GENERATING_IMAGES, 0, params); - - List<Model> models = new ArrayList<Model>(); - models.add(originalModel); - int size = originalModel.getLayouts().size(); - for (ModelSubmodelConnection connection : originalModel.getSubmodelConnections()) { - models.add(connection.getSubmodel().getModel()); - size += connection.getSubmodel().getModel().getLayouts().size(); - } - int counter = 0; - for (final Model model : models) { - for (int i = 0; i < model.getLayouts().size(); i++) { - Layout layout = model.getLayouts().get(i); - if (layout.getInputData() == null) { - final double imgCounter = counter; - final double finalSize = size; - IProgressUpdater updater = new IProgressUpdater() { - @Override - public void setProgress(double progress) { - updateProjectStatus( - originalModel.getProject(), ProjectStatus.GENERATING_IMAGES, IProgressUpdater.MAX_PROGRESS * imgCounter / finalSize + progress / finalSize, - params); - } - }; - - generateImagesForBuiltInOverlay(params, model, layout, updater); - } - counter++; - } - } - } - - private void generateImagesForBuiltInOverlay(final CreateProjectParams params, final Model model, Layout layout, IProgressUpdater updater) - throws CommandExecutionException, IOException, DrawingException { - String directory = layout.getDirectory(); - Model output = model; - if (layout.isHierarchicalView()) { - output = new CopyCommand(model).execute(); - new SetFixedHierarchyLevelCommand(output, layout.getHierarchyViewLevel()).execute(); - } - if (layout.getTitle().equals(BuildInLayout.CLEAN.getTitle())) { - output = new CopyCommand(model).execute(); - new ClearColorModelCommand(output).execute(); - } - - MapGeneratorParams imgParams = generator.new MapGeneratorParams() - .directory(directory).sbgn(params.isSbgnFormat()).nested(layout.isHierarchicalView()).updater(updater); - imgParams.model(output); - generator.generateMapImages(imgParams); - } - - /** - * Creates project. Loads model from the input and run PostLoadModification. - * - * @param params - * params used to create model - * @param dbProject - * project where the model should be placed - * @throws InvalidInputDataExecption - * thrown when there is a problem with input file - */ - protected void createModel(final CreateProjectParams params, Project dbProject) throws InvalidInputDataExecption { - ModelData modelData = modelDao.getLastModelForProjectIdentifier(params.getProjectId(), false); - if (modelData != null) { - throw new InvalidArgumentException("Model with the given name already exists"); - } - - final Project project = dbProject; - updateProjectStatus(project, ProjectStatus.PARSING_DATA, 0.0, params); - - if (params.isComplex()) { - try { - Class<? extends IConverter> clazz = CellDesignerXmlParser.class; - if (params.getParser() != null) { - clazz = params.getParser().getClass(); - } - ComplexZipConverter parser = new ComplexZipConverter(clazz); - ComplexZipConverterParams complexParams; - complexParams = new ComplexZipConverterParams().zipFile(params.getProjectFile()); - complexParams.visualizationDir(params.getProjectDir()); - for (ZipEntryFile entry : params.getZipEntries()) { - complexParams.entry(entry); - } - ProjectFactory projectFactory = new ProjectFactory(parser); - projectFactory.create(complexParams, dbProject); - } catch (IOException e) { - throw new InvalidInputDataExecption(e); - } - } else { - IConverter parser; - if (params.getParser() != null) { - parser = params.getParser(); - } else if (params.getProjectFile().endsWith("sbgn")) { - parser = new SbgnmlXmlConverter(); - } else { - parser = new CellDesignerXmlParser(); - } - try { - Model model = parser - .createModel(new ConverterParams().filename(params.getProjectFile()).sizeAutoAdjust(params.isAutoResize()).sbgnFormat(params.isSbgnFormat())); - model.setName(params.getProjectName()); - project.addModel(model); - } catch (FileNotFoundException ex) { - throw new InvalidInputDataExecption(ex); - } - } - Model topModel = project.getModels().iterator().next().getModel(); - topModel.setZoomLevels(generator.computeZoomLevels(topModel)); - topModel.setTileSize(MapGenerator.TILE_SIZE); - dbProject.setNotifyEmail(params.getNotifyEmail()); - - updateProjectStatus(project, ProjectStatus.UPLOADING_TO_DB, 0.0, params); - dbUtils.setAutoFlush(false); - projectDao.update(project); - dbUtils.setAutoFlush(true); - projectDao.flush(); - - List<BuildInLayout> buildInLayouts = new ArrayList<>(); - BuildInLayout nested = null; - if (params.isSemanticZoom()) { - nested = BuildInLayout.SEMANTIC; - } else { - nested = BuildInLayout.NESTED; - } - if (params.isNetworkLayoutAsDefault()) { - buildInLayouts.add(BuildInLayout.NORMAL); - buildInLayouts.add(nested); - } else { - buildInLayouts.add(nested); - buildInLayouts.add(BuildInLayout.NORMAL); - } - buildInLayouts.add(BuildInLayout.CLEAN); - - // reverse the order of build in layouts, so we can insert them at the - // beginning of list of layouts (the order will be the same) - Collections.reverse(buildInLayouts); - - for (BuildInLayout buildInLayout : buildInLayouts) { - Layout topLayout = new Layout( - buildInLayout.getTitle(), params.getProjectDir() + "/" + buildInLayout.getDirectorySuffix() + topModel.getId() + "/", true); - topLayout.setStatus(LayoutStatus.NA); - topLayout.setProgress(0.0); - topLayout.setHierarchicalView(buildInLayout.isNested()); - topModel.addLayout(0, topLayout); - int submodelId = 1; - List<Layout> semanticLevelOverlays = new ArrayList<>(); - if (buildInLayout.equals(BuildInLayout.SEMANTIC)) { - for (int i = 0; i <= topModel.getZoomLevels(); i++) { - String directory = params.getProjectDir() + "/" + buildInLayout.getDirectorySuffix() + "-" + i + "-" + topModel.getId() + "/"; - Layout semanticOverlay = new Layout(buildInLayout.getTitle() + "-" + i, directory, true); - semanticOverlay.setStatus(LayoutStatus.NA); - semanticOverlay.setProgress(0.0); - semanticOverlay.setHierarchicalView(buildInLayout.isNested()); - semanticOverlay.setHierarchyViewLevel(i); - semanticLevelOverlays.add(semanticOverlay); - topModel.addLayout(1, semanticOverlay); - } - } - for (ModelSubmodelConnection connection : topModel.getSubmodelConnections()) { - Layout layout = new Layout(buildInLayout.getTitle(), params.getProjectDir() + "/" + buildInLayout.getDirectorySuffix() + submodelId + "/", true); - layout.setStatus(LayoutStatus.NA); - layout.setProgress(0.0); - layout.setHierarchicalView(buildInLayout.isNested()); - layout.setParentLayout(topLayout); - connection.getSubmodel().addLayout(0, layout); - - connection.getSubmodel().setZoomLevels(generator.computeZoomLevels(connection.getSubmodel().getModel())); - connection.getSubmodel().setTileSize(MapGenerator.TILE_SIZE); - if (buildInLayout.equals(BuildInLayout.SEMANTIC)) { - for (int i = 0; i <= topModel.getZoomLevels(); i++) { - String directory = params.getProjectDir() + "/" + buildInLayout.getDirectorySuffix() + "-" + i + "-" + submodelId + "/"; - Layout semanticOverlay = new Layout(buildInLayout.getTitle() + "-" + i, directory, true); - semanticOverlay.setStatus(LayoutStatus.NA); - semanticOverlay.setProgress(0.0); - semanticOverlay.setHierarchicalView(buildInLayout.isNested()); - semanticOverlay.setParentLayout(semanticLevelOverlays.get(i)); - semanticOverlay.setHierarchyViewLevel(i); - connection.getSubmodel().addLayout(1, semanticOverlay); - } - } - submodelId++; - } - } - - if (params.isUpdateAnnotations()) { - Map<Class<?>, List<ElementAnnotator>> annotators = null; - if (params.getAnnotatorsMap() != null) { - annotators = new HashMap<Class<?>, List<ElementAnnotator>>(); - for (Class<?> clazz : params.getAnnotatorsMap().keySet()) { - annotators.put(clazz, modelAnnotator.getAnnotatorsFromCommonNames(params.getAnnotatorsMap().get(clazz))); - } - } - logger.debug("Updating annotations"); - modelAnnotator.performAnnotations(topModel, new IProgressUpdater() { - - @Override - public void setProgress(final double progress) { - updateProjectStatus(project, ProjectStatus.ANNOTATING, progress, params); - } - }, annotators); - logger.debug("Annotations updated"); - } - updateProjectStatus(project, ProjectStatus.EXTENDING_MODEL, 0.0, params); - processDataMining(topModel, topModel.getDataMiningSets(), new IProgressUpdater() { - - @Override - public void setProgress(double progress) { - updateProjectStatus(project, ProjectStatus.EXTENDING_MODEL, progress, params); - } - - }); - updateProjectStatus(project, ProjectStatus.EXTENDING_MODEL, IProgressUpdater.MAX_PROGRESS, params); - logger.debug("Model created"); - - } - - /** - * Process data mining files and assign suggested connections to the model. - * - * @param model - * model where the suggested connections will be added - * @param dataMiningSets - * set of files to process - * @param progressUpdater - * callback function informing higher layer about progress - * @throws InvalidDataMiningInputFile - * thrown when one of the files is invalid - */ - private void processDataMining(Model model, List<DataMiningSet> dataMiningSets, final IProgressUpdater progressUpdater) throws InvalidDataMiningInputFile { - Set<Element> nodes = new HashSet<>(); - nodes.addAll(model.getElements()); - for (ModelSubmodelConnection connection : model.getSubmodelConnections()) { - nodes.addAll(connection.getSubmodel().getElements()); - } - Set<Reaction> reactions = new HashSet<Reaction>(); - reactions.addAll(model.getReactions()); - for (ModelSubmodelConnection connection : model.getSubmodelConnections()) { - reactions.addAll(connection.getSubmodel().getReactions()); - } - - int fileCounter = 0; - final int filesCount = dataMiningSets.size(); - for (DataMiningSet dmSet : dataMiningSets) { - - final double offset = IProgressUpdater.MAX_PROGRESS * fileCounter / filesCount; - IProgressUpdater secondPartUpdater = new IProgressUpdater() { - - @Override - public void setProgress(double progress) { - progressUpdater.setProgress((progress * CREATION_OF_DATA) / ((double) filesCount) + offset); - } - }; - Set<DataMining> result = dataMiningService.parseData(dmSet, nodes, reactions, secondPartUpdater); - double size = result.size(); - double count = 0; - - double updateOffset = IProgressUpdater.MAX_PROGRESS * fileCounter / filesCount + CREATION_OF_DATA / ((double) filesCount); - for (DataMining missingConnection : result) { - missingConnection.setType(dmSet.getType()); - missingConnection.getElement().addDataMining(missingConnection); - count++; - progressUpdater.setProgress(updateOffset + IProgressUpdater.MAX_PROGRESS * count / size * UPLOAD_OF_DATA); - } - } - } - - /** - * Updates status of the generating project. - * - * @param project - * project that is generated - * @param status - * what is the current status - * @param progress - * what is the progress - * @param params - * parameters used for project creation - */ - private void updateProjectStatus(Project project, ProjectStatus status, double progress, CreateProjectParams params) { - if (project != null) { - if (!status.equals(project.getStatus()) || (Math.abs(progress - project.getProgress()) > IProgressUpdater.PROGRESS_BAR_UPDATE_RESOLUTION)) { - project.setStatus(status); - project.setProgress(progress); - projectDao.update(project); - if (params.isAsync()) { - projectDao.commit(); - } - } - } else { - logger.debug("status: " + status + ", " + progress); - } - } - - @Override - public void createProject(final CreateProjectParams params) throws SecurityException { - if (!userService.userHasPrivilege(params.getAuthenticationToken(), PrivilegeType.ADD_MAP)) { - throw new SecurityException("Adding projects not allowed."); - } - - // this count down is used to wait for asynchronous thread to initialize - // data in the db (probably it would be better to move the initialization to - // main thread) - final CountDownLatch waitForInitialData = new CountDownLatch(1); - - Thread computations = new Thread(new Runnable() { - - @Override - public void run() { - if (params.isAsync()) { - // because we are running this in separate thread we need to open a - // new session for db connection - dbUtils.createSessionForCurrentThread(); - } - - Project project = new Project(); - project.setProjectId(params.getProjectId()); - project.setName(params.getProjectName()); - if (params.getProjectDir() == null) { - logger.warn("Project directory not set"); - project.setDirectory(null); - } else { - project.setDirectory(new File(params.getProjectDir()).getName()); - } - project.setSbgnFormat(params.isSbgnFormat()); - - MiriamData disease = null; - if (params.getDisease() != null && !params.getDisease().isEmpty()) { - disease = new MiriamData(MiriamType.MESH_2012, params.getDisease()); - } - MiriamData organism = null; - if (params.getOrganism() != null && !params.getOrganism().isEmpty()) { - organism = new MiriamData(MiriamType.TAXONOMY, params.getOrganism()); - } - project.setVersion(params.getVersion()); - projectDao.add(project); - if (params.isAsync()) { - projectDao.commit(); - } - waitForInitialData.countDown(); - double[] outOfMemoryBuffer; - EventStorageLoggerAppender appender = new EventStorageLoggerAppender(); - try { - Logger.getRootLogger().addAppender(appender); - logger.debug("Running: " + params.getProjectId() + "; " + params.getProjectFile()); - outOfMemoryBuffer = new double[OUT_OF_MEMORY_BACKUP_BUFFER_SIZE]; - for (int i = 0; i < OUT_OF_MEMORY_BACKUP_BUFFER_SIZE; i++) { - outOfMemoryBuffer[i] = Math.random() * OUT_OF_MEMORY_BACKUP_BUFFER_SIZE; - } - - File inputFile = new File(params.getProjectFile()); - if (inputFile.exists()) { - UploadedFileEntry file = new UploadedFileEntry(); - file.setFileContent(IOUtils.toByteArray(new FileInputStream(inputFile))); - file.setOriginalFileName(FilenameUtils.getName(params.getProjectFile())); - project.setInputData(file); - } - - createModel(params, project); - Model originalModel = project.getModels().iterator().next().getModel(); - if (!params.isSemanticZoom()) { - new CreateHierarchyCommand(originalModel, generator.computeZoomLevels(originalModel), generator.computeZoomFactor(originalModel)).execute(); - } - - addUsers(project, params); - createImages(originalModel, params); - - for (Layout layout : originalModel.getLayouts()) { - String[] tmp = layout.getDirectory().split("[\\\\/]"); - layout.setDirectory(tmp[tmp.length - 1]); - } - for (ModelSubmodelConnection connection : originalModel.getSubmodelConnections()) { - for (Layout layout : connection.getSubmodel().getLayouts()) { - String[] tmp = layout.getDirectory().split("[\\\\/]"); - layout.setDirectory(tmp[tmp.length - 1]); - } - } - - try { - if (meshParser.isValidMeshId(disease)) { - project.setDisease(disease); - } else { - logger.warn("No valid disease is provided for project:" + project.getName()); - } - } catch (AnnotatorException e1) { - logger.warn("Problem with accessing mesh db. More info in logs."); - } - - if (taxonomyBackend.getNameForTaxonomy(organism) != null) { - project.setOrganism(organism); - } else { - logger.warn("No valid organism is provided for project:" + project.getName()); - } - - modelDao.update(originalModel); - - if (params.isCacheModel()) { - cacheData(originalModel, params); - } - - if (params.isAnalyzeAnnotations()) { - analyzeAnnotations(originalModel, params); - } - Logger.getRootLogger().removeAppender(appender); - project.addLoggingInfo(appender); - updateProjectStatus(project, ProjectStatus.DONE, IProgressUpdater.MAX_PROGRESS, params); - if (project.getNotifyEmail() != null && !project.getNotifyEmail().equals("")) { - try { - sendSuccesfullEmail(originalModel); - } catch (MessagingException e) { - logger.error(e, e); - } - } - - LogParams params = new LogParams().description("Created successfully").type(LogType.MAP_CREATED).object(originalModel); - logService.log(params); - - } catch (HibernateException e) { - outOfMemoryBuffer = null; - logger.error("Problem with database", e); - handleHibernateExceptionReporting(params, e); - } catch (Exception e) { - outOfMemoryBuffer = null; - handleCreateProjectException(params, e); - } catch (OutOfMemoryError oome) { - // release some memory - outOfMemoryBuffer = null; - logger.error("Out of memory", oome); - if (project != null) { - project.setErrors("Out of memory: " + oome.getMessage()); - } - updateProjectStatus(project, ProjectStatus.FAIL, IProgressUpdater.MAX_PROGRESS, params); - } finally { - if (params.isAsync()) { - // close the transaction for this thread - dbUtils.closeSessionForCurrentThread(); - } - Logger.getRootLogger().removeAppender(appender); - } - } - }); - if (params.isAsync()) { - computations.start(); - } else { - computations.run(); - } - - try { - waitForInitialData.await(); - } catch (InterruptedException e1) { - logger.error(e1, e1); - } - - } - - /** - * Cache pubmed data for the model. - * - * @param originalModel - * model for which we want to cache data. - * @param params - * parameters used for model generation - */ - private void cacheData(final Model originalModel, final CreateProjectParams params) { - modelService.cacheAllPubmedIds(originalModel, new IProgressUpdater() { - @Override - public void setProgress(double progress) { - updateProjectStatus(originalModel.getProject(), ProjectStatus.CACHING, progress, params); - } - }); - modelService.cacheAllMiriamLinks(originalModel, new IProgressUpdater() { - @Override - public void setProgress(double progress) { - updateProjectStatus(originalModel.getProject(), ProjectStatus.CACHING_MIRIAM, progress, params); - } - }); - - chemicalService.cacheDataForModel(originalModel, new IProgressUpdater() { - @Override - public void setProgress(double progress) { - updateProjectStatus(originalModel.getProject(), ProjectStatus.CACHING_CHEMICAL, progress, params); - } - }); - - drugService.cacheDataForModel(originalModel, new IProgressUpdater() { - @Override - public void setProgress(double progress) { - updateProjectStatus(originalModel.getProject(), ProjectStatus.CACHING_DRUG, progress, params); - } - }); - - mirnaService.cacheDataForModel(originalModel, new IProgressUpdater() { - @Override - public void setProgress(double progress) { - updateProjectStatus(originalModel.getProject(), ProjectStatus.CACHING_MI_RNA, progress, params); - } - }); - - } - - /** - * @return the modelDao - * @see #modelDao - */ - public ModelDao getModelDao() { - return modelDao; - } - - /** - * @param modelDao - * the modelDao to set - * @see #modelDao - */ - public void setModelDao(ModelDao modelDao) { - this.modelDao = modelDao; - } - - /** - * @return the commentService - * @see #commentService - */ - public ICommentService getCommentService() { - return commentService; - } - - /** - * @param commentService - * the commentService to set - * @see #commentService - */ - public void setCommentService(ICommentService commentService) { - this.commentService = commentService; - } - - /** - * @return the passwordEncoder - * @see #passwordEncoder - */ - public PasswordEncoder getPasswordEncoder() { - return passwordEncoder; - } - - /** - * @param passwordEncoder - * the passwordEncoder to set - * @see #passwordEncoder - */ - public void setPasswordEncoder(PasswordEncoder passwordEncoder) { - this.passwordEncoder = passwordEncoder; - } - - /** - * @return the commentDao - * @see #commentDao - */ - public CommentDao getCommentDao() { - return commentDao; - } - - /** - * @param commentDao - * the commentDao to set - * @see #commentDao - */ - public void setCommentDao(CommentDao commentDao) { - this.commentDao = commentDao; - } - - /** - * @return the modelAnnotator - * @see #modelAnnotator - */ - public ModelAnnotator getModelAnnotator() { - return modelAnnotator; - } - - /** - * @param modelAnnotator - * the modelAnnotator to set - * @see #modelAnnotator - */ - public void setModelAnnotator(ModelAnnotator modelAnnotator) { - this.modelAnnotator = modelAnnotator; - } - - /** - * @return the dbUtils - * @see #dbUtils - */ - public DbUtils getDbUtils() { - return dbUtils; - } - - /** - * @param dbUtils - * the dbUtils to set - * @see #dbUtils - */ - public void setDbUtils(DbUtils dbUtils) { - this.dbUtils = dbUtils; - } - - /** - * @return the projectDao - * @see #projectDao - */ - public ProjectDao getProjectDao() { - return projectDao; - } - - /** - * @param projectDao - * the projectDao to set - * @see #projectDao - */ - public void setProjectDao(ProjectDao projectDao) { - this.projectDao = projectDao; - } - - /** - * @return the dataMiningService - * @see #dataMiningService - */ - public IDataMiningService getDataMiningService() { - return dataMiningService; - } - - /** - * @param dataMiningService - * the dataMiningService to set - * @see #dataMiningService - */ - public void setDataMiningService(IDataMiningService dataMiningService) { - this.dataMiningService = dataMiningService; - } - - @Override - public ProjectView createEmpty() { - return projectViewFactory.create(null); - } - - /** - * Analyzes annotation of the model and put information about invalid - * annotations into the {@link Model#creationWarnings} field. - * - * @param originalModel - * model to analyze - * @param params - * parameters used for model generation - */ - protected void analyzeAnnotations(final Model originalModel, final CreateProjectParams params) { - logger.debug("Analyze annotations"); - Collection<? extends ProblematicAnnotation> improperAnnotations = modelAnnotator.findImproperAnnotations(originalModel, new IProgressUpdater() { - - @Override - public void setProgress(double progress) { - updateProjectStatus(originalModel.getProject(), ProjectStatus.VALIDATING_MIRIAM, progress, params); - } - }, params.getValidAnnotations()); - List<String> res = new ArrayList<>(); - for (ProblematicAnnotation improperAnnotation : improperAnnotations) { - res.add(improperAnnotation.toString()); - } - Collections.sort(res); - - Collection<? extends ProblematicAnnotation> missingAnnotations = modelAnnotator.findMissingAnnotations(originalModel, params.getRequiredAnnotations()); - for (ProblematicAnnotation improperAnnotation : missingAnnotations) { - res.add(improperAnnotation.toString()); - } - for (String message : res) { - logger.warn(message); - } - logger.debug("Analyze finished"); - } - - /** - * Sends notification email that map was removed. - * - * @param projectId - * identifier of the project - * @param email - * otification email - * @throws MessagingException - * thrown when there is a problem with sending email - */ - protected void sendSuccesfullRemoveEmail(String projectId, String email) throws MessagingException { - EmailSender emailSender = new EmailSender(configurationService); - StringBuilder content = new StringBuilder("Map " + projectId + " was successfully removed.<br/>"); - emailSender.sendEmail("MapViewer notification", content.toString(), email); - } - - @Override - public TreeNode createClassAnnotatorTree(User user) { - - UserAnnotationSchema annotationSchema = prepareUserAnnotationSchema(user); - - ElementUtils elementUtils = new ElementUtils(); - - ClassTreeNode top = elementUtils.getAnnotatedElementClassTree(); - - Class<?> clazz = top.getClazz(); - TreeNode root = new DefaultTreeNode( - new AnnotatedObjectTreeRow( - top, modelAnnotator.getAvailableAnnotators(clazz), modelAnnotator.getAnnotatorsFromCommonNames(annotationSchema.getAnnotatorsForClass(clazz)), - annotationSchema.getValidAnnotations(clazz), annotationSchema.getRequiredAnnotations(clazz)), - null); - - root.setExpanded(true); - - Queue<Pair<ClassTreeNode, TreeNode>> nodes = new LinkedList<Pair<ClassTreeNode, TreeNode>>(); - nodes.add(new Pair<ClassTreeNode, TreeNode>(top, root)); - // create children - - Queue<TreeNode> expandParents = new LinkedList<TreeNode>(); - - while (!nodes.isEmpty()) { - Pair<ClassTreeNode, TreeNode> element = nodes.poll(); - - for (ClassTreeNode node : element.getLeft().getChildren()) { - - clazz = node.getClazz(); - AnnotatedObjectTreeRow data = new AnnotatedObjectTreeRow( - node, modelAnnotator.getAvailableAnnotators(clazz), modelAnnotator.getAnnotatorsFromCommonNames(annotationSchema.getAnnotatorsForClass(clazz)), - annotationSchema.getValidAnnotations(clazz), annotationSchema.getRequiredAnnotations(clazz)); - TreeNode treeNode = new DefaultTreeNode(data, element.getRight()); - nodes.add(new Pair<ClassTreeNode, TreeNode>(node, treeNode)); - if (data.getUsedAnnotators().size() > 0 || data.getValidAnnotators().size() > 0) { - expandParents.add(treeNode); - } - } - } - while (!expandParents.isEmpty()) { - TreeNode node = expandParents.poll(); - if (node.getParent() != null && !node.getParent().isExpanded()) { - node.getParent().setExpanded(true); - expandParents.add(node.getParent()); - } - } - - return root; - - } - - /** - * Retrieves (or creates) annotation schema for a given user. - * - * @param user - * for this users {@link UserAnnotationSchema} will be prepared - * @return {@link UserAnnotationSchema} for {@link User} - */ - public UserAnnotationSchema prepareUserAnnotationSchema(User user) { - UserAnnotationSchema annotationSchema = null; - if (user != null) { - annotationSchema = userDao.getById(user.getId()).getAnnotationSchema(); - } - if (annotationSchema == null) { - annotationSchema = new UserAnnotationSchema(); - - ElementUtils elementUtils = new ElementUtils(); - - ClassTreeNode top = elementUtils.getAnnotatedElementClassTree(); - - Map<Class<? extends BioEntity>, Set<MiriamType>> validMiriam = modelAnnotator.getDefaultValidClasses(); - Map<Class<? extends BioEntity>, Set<MiriamType>> requiredMiriam = modelAnnotator.getDefaultRequiredClasses(); - - Queue<ClassTreeNode> nodes = new LinkedList<ClassTreeNode>(); - nodes.add(top); - - while (!nodes.isEmpty()) { - ClassTreeNode element = nodes.poll(); - annotationSchema.addClassAnnotator(new UserClassAnnotators(element.getClazz(), modelAnnotator.getAvailableDefaultAnnotatorNames(element.getClazz()))); - annotationSchema.addClassValidAnnotations(new UserClassValidAnnotations(element.getClazz(), validMiriam.get(element.getClazz()))); - annotationSchema.addClassRequiredAnnotations(new UserClassRequiredAnnotations(element.getClazz(), requiredMiriam.get(element.getClazz()))); - for (ClassTreeNode node : element.getChildren()) { - nodes.add(node); - } - } - User dbUser = userDao.getById(user.getId()); - dbUser.setAnnotationSchema(annotationSchema); - userDao.update(dbUser); - } - return annotationSchema; - } - - @Override - public void updateClassAnnotatorTreeForUser(User user, TreeNode annotatorsTree, boolean sbgnFormat, boolean networkLayoutAsDefault) { - User dbUser = userDao.getById(user.getId()); - if (dbUser.getAnnotationSchema() == null) { - dbUser.setAnnotationSchema(new UserAnnotationSchema()); - } - UserAnnotationSchema annotationSchema = dbUser.getAnnotationSchema(); - - Queue<TreeNode> queue = new LinkedList<TreeNode>(); - queue.add(annotatorsTree); - while (!queue.isEmpty()) { - TreeNode node = queue.poll(); - for (TreeNode child : node.getChildren()) { - queue.add(child); - } - AnnotatedObjectTreeRow data = (AnnotatedObjectTreeRow) node.getData(); - annotationSchema.addClassAnnotator(new UserClassAnnotators(data.getClazz(), data.getUsedAnnotators())); - annotationSchema.addClassRequiredAnnotations(new UserClassRequiredAnnotations(data.getClazz(), data.getRequiredAnnotations())); - annotationSchema.addClassValidAnnotations(new UserClassValidAnnotations(data.getClazz(), data.getValidAnnotations())); - } - annotationSchema.setSbgnFormat(sbgnFormat); - annotationSchema.setNetworkLayoutAsDefault(networkLayoutAsDefault); - userService.updateUser(dbUser); - user.setAnnotationSchema(annotationSchema); - } - - /** - * Sends email about unsuccessfull project creation. - * - * @param projectName - * name of the project - * @param email - * email where we want to send information - * @param e - * exception that caused problem - */ - private void sendUnsuccesfullEmail(String projectName, String email, Exception e) { - EmailSender emailSender = new EmailSender(configurationService); - StringBuilder content = new StringBuilder(""); - content.append("There was a problem with generating " + projectName + " map.<br/>"); - content.append(e.getClass().getName() + ": " + e.getMessage()); - try { - emailSender.sendEmail("MapViewer notification", content.toString(), email); - } catch (MessagingException e1) { - logger.error(e1); - } - } - - /** - * Sends email about successfull project creation. - * - * @param originalModel - * model that was created - * @throws MessagingException - * exception thrown when there is a problem with sending email - */ - protected void sendSuccesfullEmail(Model originalModel) throws MessagingException { - EmailSender emailSender = new EmailSender(configurationService); - StringBuilder content = new StringBuilder("Your map was generated successfully.<br/>"); - emailSender.sendEmail("MapViewer notification", content.toString(), originalModel.getProject().getNotifyEmail()); - } - - @Override - public void updateProject(Project project) { - projectDao.update(project); - } - - /** - * This method handles situation when sever db error appeard during uploading - * of the project into database. - * - * @param params - * parameters used to create project - * @param e - * exception that occured during uploading of the project - */ - private void handleHibernateExceptionReporting(CreateProjectParams params, HibernateException e) { - // we need to open separate thread because current one thrown db exception - // and transaction is corrupetd and will be rolledback - Thread reportInSeparateThread = new Thread(new Runnable() { - - @Override - public void run() { - dbUtils.createSessionForCurrentThread(); - - try { - Project project = getProjectByProjectId(params.getProjectId(), params.getAuthenticationToken()); - String errorMessage = "Problem with uploading to database. " - + "You might violated some unhandled constraints or you run out of memory. Underlaying eror:\n" + e.getMessage() - + "\nMore information can be found in log file."; - project.setErrors(errorMessage); - project.setStatus(ProjectStatus.FAIL); - projectDao.update(project); - } catch (Exception e) { - logger.error(e, e); - } finally { - dbUtils.closeSessionForCurrentThread(); - } - } - - }); - reportInSeparateThread.start(); - - } - - @Override - public byte[] getInputDataForProject(ProjectView projectView) { - return getInputDataForProject(projectView.getIdObject()); - } - - /** - * Returns byte array containing data from original input file that was used - * to create this project. - * - * @param projectId - * identifier of project for which we want to retrieve original file - * data - * @return original data file for given layout, if such file is not stored in - * database (compatibility reasons) then null is returned - */ - private byte[] getInputDataForProject(int projectId) { - Project project = projectDao.getById(projectId); - if (project == null || project.getInputData() == null) { - return null; - } else { - return project.getInputData().getFileContent(); - } - } - - /** - * Method that handles exception reporting during creation of a project. - * - * @param params - * set of parameters used to create project - * @param e - * exception that caused problems - */ - private void handleCreateProjectException(final CreateProjectParams params, Exception e) { - Project p = projectDao.getProjectByProjectId(params.getProjectId()); - logger.error(e.getMessage(), e); - if (p != null) { - p.setErrors("Problem with uploading map: " + e.getMessage() + ". More details can be found in log file."); - } - updateProjectStatus(p, ProjectStatus.FAIL, IProgressUpdater.MAX_PROGRESS, params); - - String email = params.getNotifyEmail(); - String projectName = params.getProjectId(); - - LogParams logParams = new LogParams().description("Failed: " + e.getMessage()).type(LogType.MAP_CREATED).object(p); - logService.log(logParams); - if (email != null) { - sendUnsuccesfullEmail(projectName, email, e); - } - } - - /** - * @return the taxonomyBackend - * @see #taxonomyBackend - */ - public TaxonomyBackend getTaxonomyBackend() { - return taxonomyBackend; - } - - /** - * @param taxonomyBackend - * the taxonomyBackend to set - * @see #taxonomyBackend - */ - public void setTaxonomyBackend(TaxonomyBackend taxonomyBackend) { - this.taxonomyBackend = taxonomyBackend; - } + /** + * Size of the artifitial buffer that will be released when + * {@link OutOfMemoryError} is thrown to gain some free memory and report + * problem. + */ + private static final int OUT_OF_MEMORY_BACKUP_BUFFER_SIZE = 10000; + + /** + * How much time (out of 1.00) is used for creation of the data from file. + */ + private static final double CREATION_OF_DATA = 0.50; + + /** + * How much time (out of 1.00) is used for upploading of the data from file. + */ + private static final double UPLOAD_OF_DATA = 0.50; + + /** + * Default class logger. + */ + private Logger logger = Logger.getLogger(ProjectService.class); + + /** + * Data access object for projects. + */ + @Autowired + private ProjectDao projectDao; + + /** + * Data access object for models. + */ + @Autowired + private ModelDao modelDao; + + /** + * Data access object for users. + */ + @Autowired + private UserDao userDao; + + /** + * Service that allows to access and manage models. + */ + @Autowired + private IModelService modelService; + + /** + * Service that allows to access and manage comments. + */ + @Autowired + private ICommentService commentService; + + /** + * Service used to access logs. + * + * @see ILogService + */ + @Autowired + private ILogService logService; + + /** + * Service that manages and gives access to configuration parameters. + */ + @Autowired + private IConfigurationService configurationService; + + /** + * Services that manages and gives access to user information. + */ + @Autowired + private IUserService userService; + + /** + * Services that accessa data about chemicals. + */ + @Autowired + private IChemicalService chemicalService; + + /** + * Services that accessa data about drugs. + */ + @Autowired + private IDrugService drugService; + + /** + * Services that accessa data about mirna. + */ + @Autowired + private IMiRNAService mirnaService; + + /** + * Service that manages data mining information. + */ + @Autowired + private IDataMiningService dataMiningService; + + /** + * Data access object for comments. + */ + @Autowired + private CommentDao commentDao; + + /** + * Service that provides password encoding. + */ + @Autowired + private PasswordEncoder passwordEncoder; + + /** + * Module that allows to annotate maps. + */ + @Autowired + private ModelAnnotator modelAnnotator; + + /** + * Utils that help to manage the sessions in custom multithreaded + * implementation. + */ + @Autowired + private DbUtils dbUtils; + + /** + * Factory object for {@link ProjectView} elements. + */ + @Autowired + private ProjectViewFactory projectViewFactory; + + /** + * Access point and parser for the online ctd database. + */ + @Autowired + private MeSHParser meshParser; + + /** + * Access point and parser for the online ctd database. + */ + @Autowired + private TaxonomyBackend taxonomyBackend; + + /** + * Class that helps to generate images for google maps API. + */ + private MapGenerator generator = new MapGenerator(); + + @Override + public Project getProjectByProjectId(String name, AuthenticationToken token) throws UserAccessException { + Project result = projectDao.getProjectByProjectId(name); + if (result == null) { + return result; + } + if (userService.userHasPrivilege(token, PrivilegeType.VIEW_PROJECT, result)) { + return result; + } else if (userService.userHasPrivilege(token, PrivilegeType.ADD_MAP)) { + return result; + } + throw new UserAccessException("User cannot access project"); + } + + @Override + public boolean projectExists(String projectName) { + if (projectName == null || projectName.equals("")) { + return false; + } + return projectDao.isProjectExistsByName(projectName); + } + + @Override + public List<Project> getAllProjects(AuthenticationToken token) { + List<Project> projects = projectDao.getAll(); + if (userService.userHasPrivilege(token, PrivilegeType.ADD_MAP)) { + return projects; + } + List<Project> result = new ArrayList<>(); + for (Project project : projects) { + if (userService.userHasPrivilege(token, PrivilegeType.VIEW_PROJECT, project)) { + result.add(project); + } + } + return result; + } + + @Override + public List<ProjectView> getAllProjectViews(AuthenticationToken token) { + List<ProjectView> result = new ArrayList<>(); + List<Project> projects = getAllProjects(token); + for (Project project : projects) { + result.add(projectViewFactory.create(project)); + } + return result; + } + + @Override + public void updateProject(ProjectView selectedProject) { + Project project = projectDao.getById(selectedProject.getIdObject()); + project.setProjectId(selectedProject.getProjectId()); + project.setName(selectedProject.getProjectName()); + project.setVersion(selectedProject.getVersion()); + project.setNotifyEmail(selectedProject.getNotifyEmail()); + MiriamData disease = null; + MiriamData organism = null; + if (selectedProject.getNewDiseaseName() != null && !selectedProject.getNewDiseaseName().isEmpty()) { + disease = new MiriamData(MiriamType.MESH_2012, selectedProject.getNewDiseaseName()); + } + if (selectedProject.getNewOrganismName() != null && !selectedProject.getNewOrganismName().isEmpty()) { + organism = new MiriamData(MiriamType.TAXONOMY, selectedProject.getNewOrganismName()); + } + try { + if (meshParser.isValidMeshId(disease)) { + project.setDisease(disease); + } else { + project.setDisease(null); + selectedProject.setNewDiseaseName(null); + } + } catch (AnnotatorException e) { + logger.error("Problem with accessing mesh", e); + project.setDisease(null); + selectedProject.setNewDiseaseName(null); + } + try { + if (taxonomyBackend.getNameForTaxonomy(organism) != null) { + project.setOrganism(organism); + } else { + project.setOrganism(null); + selectedProject.setNewOrganismName(null); + } + } catch (TaxonomySearchException e) { + logger.error("Problem with accessing taxonomy db", e); + project.setOrganism(null); + selectedProject.setNewOrganismName(null); + } + for (ModelData model : project.getModels()) { + if (model.getId().equals(selectedProject.getModelId())) { + modelService.updateModel(model, selectedProject); + } + } + projectDao.update(project); + } + + @Override + public void removeProject(ProjectView selectedProject, String homeDir, boolean async, AuthenticationToken token) + throws UserAccessException { + Project project = projectDao.getById(selectedProject.getIdObject()); + removeProject(project, homeDir, async, token); + } + + /** + * Removes all types of privileges from every user to the project given in the + * parameter. + * + * @param project + * from which project we remove privileges + */ + private void removePrivilegesForProject(Project project) { + for (PrivilegeType type : PrivilegeType.values()) { + if (type.getPrivilegeObjectType() == Project.class) { + userService.dropPrivilegesForObjectType(type, project.getId()); + } + } + } + + @Override + public ProjectView getProjectViewByProjectId(String name, AuthenticationToken token) throws UserAccessException { + Project project = getProjectByProjectId(name, token); + return projectViewFactory.create(project); + } + + @Override + public void removeProject(final Project p, final String dir, final boolean async, AuthenticationToken token) + throws UserAccessException { + if (!userService.userHasPrivilege(userService.getUserByToken(token), PrivilegeType.PROJECT_MANAGEMENT)) { + throw new UserAccessException("User cannot remove project"); + } + + final String homeDir; + if (dir != null) { + if (p.getDirectory() != null) { + homeDir = dir + "/../map_images/" + p.getDirectory() + "/"; + } else { + homeDir = dir + "/../map_images/"; + } + } else { + if (p.getDirectory() != null) { + homeDir = p.getDirectory() + "/"; + } else { + homeDir = null; + } + } + removePrivilegesForProject(p); + updateProjectStatus(p, ProjectStatus.REMOVING, 0, new CreateProjectParams()); + Thread computations = new Thread(new Runnable() { + + @Override + public void run() { + if (async) { + // because we are running this in separate thread we need to open a + // new + // session for db connection + dbUtils.createSessionForCurrentThread(); + } + + Project project = projectDao.getById(p.getId()); + + try { + String email = null; + MapGenerator mapGenerator = new MapGenerator(); + for (ModelData originalModel : project.getModels()) { + List<ModelData> models = new ArrayList<ModelData>(); + models.add(originalModel); + for (ModelSubmodelConnection connection : originalModel.getSubmodels()) { + models.add(connection.getSubmodel()); + } + modelService.removeModelFromCache(originalModel); + for (ModelData model : models) { + logger.debug("Remove model: " + model.getId()); + commentService.removeCommentsForModel(model); + + for (Layout layout : model.getLayouts()) { + try { + mapGenerator.removeLayout(layout, homeDir); + } catch (IOException e) { + logger.error("Problem with removing directory: " + layout.getDirectory(), e); + } + } + } + email = project.getNotifyEmail(); + } + projectDao.delete(project); + if (async) { + projectDao.commit(); + } + + LogParams params = new LogParams().type(LogType.MAP_REMOVED).object(project); + logService.log(params); + + if (email != null) { + try { + sendSuccesfullRemoveEmail(project.getProjectId(), email); + } catch (MessagingException e) { + logger.error("Problem with sending remove email.", e); + } + } + modelService.removeModelFromCacheByProjectId(p.getProjectId()); + + } catch (HibernateException e) { + logger.error("Problem with database", e); + handleHibernateExceptionRemovingReporting(project, e, token); + } finally { + if (async) { + // close the transaction for this thread + dbUtils.closeSessionForCurrentThread(); + } + } + if (homeDir != null) { + File homeDirFile = new File(homeDir); + if (homeDirFile.exists()) { + logger.debug("Removing project directory: " + homeDirFile.getAbsolutePath()); + try { + FileUtils.deleteDirectory(homeDirFile); + } catch (IOException e) { + logger.error("Problem with removing diriectory", e); + } + } + } + + } + }); + + if (async) { + computations.start(); + } else { + computations.run(); + } + + } + + /** + * When we encountered hibernate exception we need to handle error reporting + * differently (hibernate session is broken). This method handles such case when + * hibernate excedption occured when removing project. + * + * @param originalProject + * project that was beining removed + * @param exception + * hibernate exception that caused problems + */ + protected void handleHibernateExceptionRemovingReporting(Project originalProject, HibernateException exception, + AuthenticationToken token) { + // we need to open separate thread because current one thrown db exception + // and transaction is corrupetd and will be rolledback + Thread reportInSeparateThread = new Thread(new Runnable() { + + @Override + public void run() { + dbUtils.createSessionForCurrentThread(); + try { + // we need to get the project from db, because session where + // originalProject was retrieved is broken + Project project = getProjectByProjectId(originalProject.getProjectId(), token); + String errorMessage = "Severe problem with removing object. Underlaying eror:\n" + exception.getMessage() + + "\nMore information can be found in log file."; + project.setErrors(errorMessage + "\n" + project.getErrors()); + project.setStatus(ProjectStatus.FAIL); + projectDao.update(project); + } catch (UserAccessException e) { + logger.error(e, e); + } finally { + dbUtils.closeSessionForCurrentThread(); + } + } + + }); + reportInSeparateThread.start(); + } + + @Override + public void addProject(Project project) { + projectDao.add(project); + + } + + @Override + public ProjectView getProjectViewById(Integer id, AuthenticationToken token) throws UserAccessException { + Project project = projectDao.getById(id); + if (userService.userHasPrivilege(token, PrivilegeType.VIEW_PROJECT, project)) { + return projectViewFactory.create(project); + } else if (userService.userHasPrivilege(token, PrivilegeType.ADD_MAP)) { + return projectViewFactory.create(project); + } else { + throw new UserAccessException("User cannot access project"); + } + } + + /** + * This methods add privileges for the users listed in params to the project. + * + * @param project + * to which project we add privileges + * @param params + * which users should be included to have privileges to the project + */ + private void addUsers(Project project, CreateProjectParams params) { + if (project == null) { + logger.warn("Users won't be added. Project not defined"); + return; + } + List<String[]> users = params.getUsers(); + String[][] newUsers = new String[users.size() + 1][2]; + for (int i = 0; i < users.size(); i++) { + for (int j = 0; j < 2; j++) { + newUsers[i][j] = users.get(i)[j]; + } + } + newUsers[users.size()][0] = "anonymous"; + newUsers[users.size()][1] = ""; + for (int i = 0; i < newUsers.length; i++) { + boolean admin = (users.size() != i); + String login = newUsers[i][0]; + String passwd = newUsers[i][1]; + User user = userService.getUserByLogin(login); + if (userService.getUserByLogin(login) == null) { + logger.debug("User " + login + " does not exist. Creating"); + user = new User(); + user.setCryptedPassword(passwordEncoder.encode(passwd)); + user.setLogin(login); + userService.addUser(user); + } + if (project != null) { + logger.debug("Privileges for " + login + " for project " + project.getProjectId()); + ObjectPrivilege privilege = new ObjectPrivilege(project, 1, PrivilegeType.VIEW_PROJECT, user); + userService.setUserPrivilege(user, privilege); + if (admin) { + privilege = new ObjectPrivilege(project, 1, PrivilegeType.LAYOUT_MANAGEMENT, user); + userService.setUserPrivilege(user, privilege); + privilege = new ObjectPrivilege(project, 1, PrivilegeType.EDIT_COMMENTS_PROJECT, user); + userService.setUserPrivilege(user, privilege); + } + + } + } + } + + /** + * This method creates set of images for the model layouts. + * + * @param originalModel + * model for which we create layout images + * @param params + * configuration parameters including set of layouts to generate + * @throws IOException + * thrown when there are problems with generating files + * @throws DrawingException + * thrown when there was a problem with drawing a map + * @throws CommandExecutionException + * thrown when one of the files describing layouts is invalid + */ + protected void createImages(final Model originalModel, final CreateProjectParams params) + throws IOException, DrawingException, CommandExecutionException { + if (!params.isImages()) { + return; + } + updateProjectStatus(originalModel.getProject(), ProjectStatus.GENERATING_IMAGES, 0, params); + + List<Model> models = new ArrayList<Model>(); + models.add(originalModel); + int size = originalModel.getLayouts().size(); + for (ModelSubmodelConnection connection : originalModel.getSubmodelConnections()) { + models.add(connection.getSubmodel().getModel()); + size += connection.getSubmodel().getModel().getLayouts().size(); + } + int counter = 0; + for (final Model model : models) { + for (int i = 0; i < model.getLayouts().size(); i++) { + Layout layout = model.getLayouts().get(i); + if (layout.getInputData() == null) { + final double imgCounter = counter; + final double finalSize = size; + IProgressUpdater updater = new IProgressUpdater() { + @Override + public void setProgress(double progress) { + updateProjectStatus(originalModel.getProject(), ProjectStatus.GENERATING_IMAGES, + IProgressUpdater.MAX_PROGRESS * imgCounter / finalSize + progress / finalSize, params); + } + }; + + generateImagesForBuiltInOverlay(params, model, layout, updater); + } + counter++; + } + } + } + + private void generateImagesForBuiltInOverlay(final CreateProjectParams params, final Model model, Layout layout, + IProgressUpdater updater) throws CommandExecutionException, IOException, DrawingException { + String directory = layout.getDirectory(); + Model output = model; + if (layout.isHierarchicalView()) { + output = new CopyCommand(model).execute(); + new SetFixedHierarchyLevelCommand(output, layout.getHierarchyViewLevel()).execute(); + } + if (layout.getTitle().equals(BuildInLayout.CLEAN.getTitle())) { + output = new CopyCommand(model).execute(); + new ClearColorModelCommand(output).execute(); + } + + MapGeneratorParams imgParams = generator.new MapGeneratorParams().directory(directory).sbgn(params.isSbgnFormat()) + .nested(layout.isHierarchicalView()).updater(updater); + imgParams.model(output); + generator.generateMapImages(imgParams); + } + + /** + * Creates project. Loads model from the input and run PostLoadModification. + * + * @param params + * params used to create model + * @param dbProject + * project where the model should be placed + * @throws InvalidInputDataExecption + * thrown when there is a problem with input file + */ + protected void createModel(final CreateProjectParams params, Project dbProject) throws InvalidInputDataExecption { + ModelData modelData = modelDao.getLastModelForProjectIdentifier(params.getProjectId(), false); + if (modelData != null) { + throw new InvalidArgumentException("Model with the given name already exists"); + } + + final Project project = dbProject; + updateProjectStatus(project, ProjectStatus.PARSING_DATA, 0.0, params); + + if (params.isComplex()) { + try { + Class<? extends IConverter> clazz = CellDesignerXmlParser.class; + if (params.getParser() != null) { + clazz = params.getParser().getClass(); + } + ComplexZipConverter parser = new ComplexZipConverter(clazz); + ComplexZipConverterParams complexParams; + complexParams = new ComplexZipConverterParams().zipFile(params.getProjectFile()); + complexParams.visualizationDir(params.getProjectDir()); + for (ZipEntryFile entry : params.getZipEntries()) { + complexParams.entry(entry); + } + ProjectFactory projectFactory = new ProjectFactory(parser); + projectFactory.create(complexParams, dbProject); + } catch (IOException e) { + throw new InvalidInputDataExecption(e); + } + } else { + IConverter parser; + if (params.getParser() != null) { + parser = params.getParser(); + } else if (params.getProjectFile().endsWith("sbgn")) { + parser = new SbgnmlXmlConverter(); + } else { + parser = new CellDesignerXmlParser(); + } + try { + Model model = parser.createModel(new ConverterParams().filename(params.getProjectFile()) + .sizeAutoAdjust(params.isAutoResize()).sbgnFormat(params.isSbgnFormat())); + model.setName(params.getProjectName()); + project.addModel(model); + } catch (FileNotFoundException ex) { + throw new InvalidInputDataExecption(ex); + } + } + Model topModel = project.getModels().iterator().next().getModel(); + topModel.setZoomLevels(generator.computeZoomLevels(topModel)); + topModel.setTileSize(MapGenerator.TILE_SIZE); + dbProject.setNotifyEmail(params.getNotifyEmail()); + + updateProjectStatus(project, ProjectStatus.UPLOADING_TO_DB, 0.0, params); + dbUtils.setAutoFlush(false); + projectDao.update(project); + dbUtils.setAutoFlush(true); + projectDao.flush(); + + List<BuildInLayout> buildInLayouts = new ArrayList<>(); + BuildInLayout nested = null; + if (params.isSemanticZoom()) { + nested = BuildInLayout.SEMANTIC; + } else { + nested = BuildInLayout.NESTED; + } + if (params.isNetworkLayoutAsDefault()) { + buildInLayouts.add(BuildInLayout.NORMAL); + buildInLayouts.add(nested); + } else { + buildInLayouts.add(nested); + buildInLayouts.add(BuildInLayout.NORMAL); + } + buildInLayouts.add(BuildInLayout.CLEAN); + + // reverse the order of build in layouts, so we can insert them at the + // beginning of list of layouts (the order will be the same) + Collections.reverse(buildInLayouts); + + for (BuildInLayout buildInLayout : buildInLayouts) { + Layout topLayout = new Layout(buildInLayout.getTitle(), + params.getProjectDir() + "/" + buildInLayout.getDirectorySuffix() + topModel.getId() + "/", true); + topLayout.setStatus(LayoutStatus.NA); + topLayout.setProgress(0.0); + topLayout.setHierarchicalView(buildInLayout.isNested()); + topModel.addLayout(0, topLayout); + int submodelId = 1; + List<Layout> semanticLevelOverlays = new ArrayList<>(); + if (buildInLayout.equals(BuildInLayout.SEMANTIC)) { + for (int i = 0; i <= topModel.getZoomLevels(); i++) { + String directory = params.getProjectDir() + "/" + buildInLayout.getDirectorySuffix() + "-" + i + "-" + + topModel.getId() + "/"; + Layout semanticOverlay = new Layout(buildInLayout.getTitle() + "-" + i, directory, true); + semanticOverlay.setStatus(LayoutStatus.NA); + semanticOverlay.setProgress(0.0); + semanticOverlay.setHierarchicalView(buildInLayout.isNested()); + semanticOverlay.setHierarchyViewLevel(i); + semanticLevelOverlays.add(semanticOverlay); + topModel.addLayout(1, semanticOverlay); + } + } + for (ModelSubmodelConnection connection : topModel.getSubmodelConnections()) { + Layout layout = new Layout(buildInLayout.getTitle(), + params.getProjectDir() + "/" + buildInLayout.getDirectorySuffix() + submodelId + "/", true); + layout.setStatus(LayoutStatus.NA); + layout.setProgress(0.0); + layout.setHierarchicalView(buildInLayout.isNested()); + layout.setParentLayout(topLayout); + connection.getSubmodel().addLayout(0, layout); + + connection.getSubmodel().setZoomLevels(generator.computeZoomLevels(connection.getSubmodel().getModel())); + connection.getSubmodel().setTileSize(MapGenerator.TILE_SIZE); + if (buildInLayout.equals(BuildInLayout.SEMANTIC)) { + for (int i = 0; i <= topModel.getZoomLevels(); i++) { + String directory = params.getProjectDir() + "/" + buildInLayout.getDirectorySuffix() + "-" + i + "-" + + submodelId + "/"; + Layout semanticOverlay = new Layout(buildInLayout.getTitle() + "-" + i, directory, true); + semanticOverlay.setStatus(LayoutStatus.NA); + semanticOverlay.setProgress(0.0); + semanticOverlay.setHierarchicalView(buildInLayout.isNested()); + semanticOverlay.setParentLayout(semanticLevelOverlays.get(i)); + semanticOverlay.setHierarchyViewLevel(i); + connection.getSubmodel().addLayout(1, semanticOverlay); + } + } + submodelId++; + } + } + + if (params.isUpdateAnnotations()) { + Map<Class<?>, List<ElementAnnotator>> annotators = null; + if (params.getAnnotatorsMap() != null) { + annotators = new HashMap<Class<?>, List<ElementAnnotator>>(); + for (Class<?> clazz : params.getAnnotatorsMap().keySet()) { + annotators.put(clazz, modelAnnotator.getAnnotatorsFromCommonNames(params.getAnnotatorsMap().get(clazz))); + } + } + logger.debug("Updating annotations"); + modelAnnotator.performAnnotations(topModel, new IProgressUpdater() { + + @Override + public void setProgress(final double progress) { + updateProjectStatus(project, ProjectStatus.ANNOTATING, progress, params); + } + }, annotators); + logger.debug("Annotations updated"); + } + updateProjectStatus(project, ProjectStatus.EXTENDING_MODEL, 0.0, params); + processDataMining(topModel, topModel.getDataMiningSets(), new IProgressUpdater() { + + @Override + public void setProgress(double progress) { + updateProjectStatus(project, ProjectStatus.EXTENDING_MODEL, progress, params); + } + + }); + updateProjectStatus(project, ProjectStatus.EXTENDING_MODEL, IProgressUpdater.MAX_PROGRESS, params); + logger.debug("Model created"); + + } + + /** + * Process data mining files and assign suggested connections to the model. + * + * @param model + * model where the suggested connections will be added + * @param dataMiningSets + * set of files to process + * @param progressUpdater + * callback function informing higher layer about progress + * @throws InvalidDataMiningInputFile + * thrown when one of the files is invalid + */ + private void processDataMining(Model model, List<DataMiningSet> dataMiningSets, + final IProgressUpdater progressUpdater) throws InvalidDataMiningInputFile { + Set<Element> nodes = new HashSet<>(); + nodes.addAll(model.getElements()); + for (ModelSubmodelConnection connection : model.getSubmodelConnections()) { + nodes.addAll(connection.getSubmodel().getElements()); + } + Set<Reaction> reactions = new HashSet<Reaction>(); + reactions.addAll(model.getReactions()); + for (ModelSubmodelConnection connection : model.getSubmodelConnections()) { + reactions.addAll(connection.getSubmodel().getReactions()); + } + + int fileCounter = 0; + final int filesCount = dataMiningSets.size(); + for (DataMiningSet dmSet : dataMiningSets) { + + final double offset = IProgressUpdater.MAX_PROGRESS * fileCounter / filesCount; + IProgressUpdater secondPartUpdater = new IProgressUpdater() { + + @Override + public void setProgress(double progress) { + progressUpdater.setProgress((progress * CREATION_OF_DATA) / ((double) filesCount) + offset); + } + }; + Set<DataMining> result = dataMiningService.parseData(dmSet, nodes, reactions, secondPartUpdater); + double size = result.size(); + double count = 0; + + double updateOffset = IProgressUpdater.MAX_PROGRESS * fileCounter / filesCount + + CREATION_OF_DATA / ((double) filesCount); + for (DataMining missingConnection : result) { + missingConnection.setType(dmSet.getType()); + missingConnection.getElement().addDataMining(missingConnection); + count++; + progressUpdater.setProgress(updateOffset + IProgressUpdater.MAX_PROGRESS * count / size * UPLOAD_OF_DATA); + } + } + } + + /** + * Updates status of the generating project. + * + * @param project + * project that is generated + * @param status + * what is the current status + * @param progress + * what is the progress + * @param params + * parameters used for project creation + */ + private void updateProjectStatus(Project project, ProjectStatus status, double progress, CreateProjectParams params) { + if (project != null) { + if (!status.equals(project.getStatus()) + || (Math.abs(progress - project.getProgress()) > IProgressUpdater.PROGRESS_BAR_UPDATE_RESOLUTION)) { + project.setStatus(status); + project.setProgress(progress); + projectDao.update(project); + if (params.isAsync()) { + projectDao.commit(); + } + } + } else { + logger.debug("status: " + status + ", " + progress); + } + } + + @Override + public void createProject(final CreateProjectParams params) throws SecurityException { + if (!userService.userHasPrivilege(params.getAuthenticationToken(), PrivilegeType.ADD_MAP)) { + throw new SecurityException("Adding projects not allowed."); + } + + // this count down is used to wait for asynchronous thread to initialize + // data in the db (probably it would be better to move the initialization to + // main thread) + final CountDownLatch waitForInitialData = new CountDownLatch(1); + + Thread computations = new Thread(new Runnable() { + + @Override + public void run() { + if (params.isAsync()) { + // because we are running this in separate thread we need to open a + // new session for db connection + dbUtils.createSessionForCurrentThread(); + } + + Project project = new Project(); + project.setProjectId(params.getProjectId()); + project.setName(params.getProjectName()); + if (params.getProjectDir() == null) { + logger.warn("Project directory not set"); + project.setDirectory(null); + } else { + project.setDirectory(new File(params.getProjectDir()).getName()); + } + project.setSbgnFormat(params.isSbgnFormat()); + + MiriamData disease = null; + if (params.getDisease() != null && !params.getDisease().isEmpty()) { + disease = new MiriamData(MiriamType.MESH_2012, params.getDisease()); + } + MiriamData organism = null; + if (params.getOrganism() != null && !params.getOrganism().isEmpty()) { + organism = new MiriamData(MiriamType.TAXONOMY, params.getOrganism()); + } + project.setVersion(params.getVersion()); + projectDao.add(project); + if (params.isAsync()) { + projectDao.commit(); + } + waitForInitialData.countDown(); + double[] outOfMemoryBuffer; + EventStorageLoggerAppender appender = new EventStorageLoggerAppender(); + try { + Logger.getRootLogger().addAppender(appender); + logger.debug("Running: " + params.getProjectId() + "; " + params.getProjectFile()); + outOfMemoryBuffer = new double[OUT_OF_MEMORY_BACKUP_BUFFER_SIZE]; + for (int i = 0; i < OUT_OF_MEMORY_BACKUP_BUFFER_SIZE; i++) { + outOfMemoryBuffer[i] = Math.random() * OUT_OF_MEMORY_BACKUP_BUFFER_SIZE; + } + + File inputFile = new File(params.getProjectFile()); + if (inputFile.exists()) { + UploadedFileEntry file = new UploadedFileEntry(); + file.setFileContent(IOUtils.toByteArray(new FileInputStream(inputFile))); + file.setOriginalFileName(FilenameUtils.getName(params.getProjectFile())); + file.setLength(file.getFileContent().length); + User owner = null; + for (String[] userRow : params.getUsers()) { + User user = userService.getUserByLogin(userRow[0]); + if (user != null) { + owner = user; + } + } + file.setOwner(owner); + project.setInputData(file); + } + + createModel(params, project); + Model originalModel = project.getModels().iterator().next().getModel(); + if (!params.isSemanticZoom()) { + new CreateHierarchyCommand(originalModel, generator.computeZoomLevels(originalModel), + generator.computeZoomFactor(originalModel)).execute(); + } + + addUsers(project, params); + createImages(originalModel, params); + + for (Layout layout : originalModel.getLayouts()) { + String[] tmp = layout.getDirectory().split("[\\\\/]"); + layout.setDirectory(tmp[tmp.length - 1]); + } + for (ModelSubmodelConnection connection : originalModel.getSubmodelConnections()) { + for (Layout layout : connection.getSubmodel().getLayouts()) { + String[] tmp = layout.getDirectory().split("[\\\\/]"); + layout.setDirectory(tmp[tmp.length - 1]); + } + } + + try { + if (meshParser.isValidMeshId(disease)) { + project.setDisease(disease); + } else { + logger.warn("No valid disease is provided for project:" + project.getName()); + } + } catch (AnnotatorException e1) { + logger.warn("Problem with accessing mesh db. More info in logs."); + } + + if (taxonomyBackend.getNameForTaxonomy(organism) != null) { + project.setOrganism(organism); + } else { + logger.warn("No valid organism is provided for project:" + project.getName()); + } + + modelDao.update(originalModel); + + if (params.isCacheModel()) { + cacheData(originalModel, params); + } + + if (params.isAnalyzeAnnotations()) { + analyzeAnnotations(originalModel, params); + } + Logger.getRootLogger().removeAppender(appender); + project.addLoggingInfo(appender); + updateProjectStatus(project, ProjectStatus.DONE, IProgressUpdater.MAX_PROGRESS, params); + if (project.getNotifyEmail() != null && !project.getNotifyEmail().equals("")) { + try { + sendSuccesfullEmail(originalModel); + } catch (MessagingException e) { + logger.error(e, e); + } + } + + LogParams params = new LogParams().description("Created successfully").type(LogType.MAP_CREATED) + .object(originalModel); + logService.log(params); + + } catch (HibernateException e) { + outOfMemoryBuffer = null; + logger.error("Problem with database", e); + handleHibernateExceptionReporting(params, e); + } catch (Exception e) { + outOfMemoryBuffer = null; + handleCreateProjectException(params, e); + } catch (OutOfMemoryError oome) { + // release some memory + outOfMemoryBuffer = null; + logger.error("Out of memory", oome); + if (project != null) { + project.setErrors("Out of memory: " + oome.getMessage()); + } + updateProjectStatus(project, ProjectStatus.FAIL, IProgressUpdater.MAX_PROGRESS, params); + } finally { + if (params.isAsync()) { + // close the transaction for this thread + dbUtils.closeSessionForCurrentThread(); + } + Logger.getRootLogger().removeAppender(appender); + } + } + }); + if (params.isAsync()) { + computations.start(); + } else { + computations.run(); + } + + try { + waitForInitialData.await(); + } catch (InterruptedException e1) { + logger.error(e1, e1); + } + + } + + /** + * Cache pubmed data for the model. + * + * @param originalModel + * model for which we want to cache data. + * @param params + * parameters used for model generation + */ + private void cacheData(final Model originalModel, final CreateProjectParams params) { + modelService.cacheAllPubmedIds(originalModel, new IProgressUpdater() { + @Override + public void setProgress(double progress) { + updateProjectStatus(originalModel.getProject(), ProjectStatus.CACHING, progress, params); + } + }); + modelService.cacheAllMiriamLinks(originalModel, new IProgressUpdater() { + @Override + public void setProgress(double progress) { + updateProjectStatus(originalModel.getProject(), ProjectStatus.CACHING_MIRIAM, progress, params); + } + }); + + chemicalService.cacheDataForModel(originalModel, new IProgressUpdater() { + @Override + public void setProgress(double progress) { + updateProjectStatus(originalModel.getProject(), ProjectStatus.CACHING_CHEMICAL, progress, params); + } + }); + + drugService.cacheDataForModel(originalModel, new IProgressUpdater() { + @Override + public void setProgress(double progress) { + updateProjectStatus(originalModel.getProject(), ProjectStatus.CACHING_DRUG, progress, params); + } + }); + + mirnaService.cacheDataForModel(originalModel, new IProgressUpdater() { + @Override + public void setProgress(double progress) { + updateProjectStatus(originalModel.getProject(), ProjectStatus.CACHING_MI_RNA, progress, params); + } + }); + + } + + /** + * @return the modelDao + * @see #modelDao + */ + public ModelDao getModelDao() { + return modelDao; + } + + /** + * @param modelDao + * the modelDao to set + * @see #modelDao + */ + public void setModelDao(ModelDao modelDao) { + this.modelDao = modelDao; + } + + /** + * @return the commentService + * @see #commentService + */ + public ICommentService getCommentService() { + return commentService; + } + + /** + * @param commentService + * the commentService to set + * @see #commentService + */ + public void setCommentService(ICommentService commentService) { + this.commentService = commentService; + } + + /** + * @return the passwordEncoder + * @see #passwordEncoder + */ + public PasswordEncoder getPasswordEncoder() { + return passwordEncoder; + } + + /** + * @param passwordEncoder + * the passwordEncoder to set + * @see #passwordEncoder + */ + public void setPasswordEncoder(PasswordEncoder passwordEncoder) { + this.passwordEncoder = passwordEncoder; + } + + /** + * @return the commentDao + * @see #commentDao + */ + public CommentDao getCommentDao() { + return commentDao; + } + + /** + * @param commentDao + * the commentDao to set + * @see #commentDao + */ + public void setCommentDao(CommentDao commentDao) { + this.commentDao = commentDao; + } + + /** + * @return the modelAnnotator + * @see #modelAnnotator + */ + public ModelAnnotator getModelAnnotator() { + return modelAnnotator; + } + + /** + * @param modelAnnotator + * the modelAnnotator to set + * @see #modelAnnotator + */ + public void setModelAnnotator(ModelAnnotator modelAnnotator) { + this.modelAnnotator = modelAnnotator; + } + + /** + * @return the dbUtils + * @see #dbUtils + */ + public DbUtils getDbUtils() { + return dbUtils; + } + + /** + * @param dbUtils + * the dbUtils to set + * @see #dbUtils + */ + public void setDbUtils(DbUtils dbUtils) { + this.dbUtils = dbUtils; + } + + /** + * @return the projectDao + * @see #projectDao + */ + public ProjectDao getProjectDao() { + return projectDao; + } + + /** + * @param projectDao + * the projectDao to set + * @see #projectDao + */ + public void setProjectDao(ProjectDao projectDao) { + this.projectDao = projectDao; + } + + /** + * @return the dataMiningService + * @see #dataMiningService + */ + public IDataMiningService getDataMiningService() { + return dataMiningService; + } + + /** + * @param dataMiningService + * the dataMiningService to set + * @see #dataMiningService + */ + public void setDataMiningService(IDataMiningService dataMiningService) { + this.dataMiningService = dataMiningService; + } + + @Override + public ProjectView createEmpty() { + return projectViewFactory.create(null); + } + + /** + * Analyzes annotation of the model and put information about invalid + * annotations into the {@link Model#creationWarnings} field. + * + * @param originalModel + * model to analyze + * @param params + * parameters used for model generation + */ + protected void analyzeAnnotations(final Model originalModel, final CreateProjectParams params) { + logger.debug("Analyze annotations"); + Collection<? extends ProblematicAnnotation> improperAnnotations = modelAnnotator + .findImproperAnnotations(originalModel, new IProgressUpdater() { + + @Override + public void setProgress(double progress) { + updateProjectStatus(originalModel.getProject(), ProjectStatus.VALIDATING_MIRIAM, progress, params); + } + }, params.getValidAnnotations()); + List<String> res = new ArrayList<>(); + for (ProblematicAnnotation improperAnnotation : improperAnnotations) { + res.add(improperAnnotation.toString()); + } + Collections.sort(res); + + Collection<? extends ProblematicAnnotation> missingAnnotations = modelAnnotator + .findMissingAnnotations(originalModel, params.getRequiredAnnotations()); + for (ProblematicAnnotation improperAnnotation : missingAnnotations) { + res.add(improperAnnotation.toString()); + } + for (String message : res) { + logger.warn(message); + } + logger.debug("Analyze finished"); + } + + /** + * Sends notification email that map was removed. + * + * @param projectId + * identifier of the project + * @param email + * otification email + * @throws MessagingException + * thrown when there is a problem with sending email + */ + protected void sendSuccesfullRemoveEmail(String projectId, String email) throws MessagingException { + EmailSender emailSender = new EmailSender(configurationService); + StringBuilder content = new StringBuilder("Map " + projectId + " was successfully removed.<br/>"); + emailSender.sendEmail("MapViewer notification", content.toString(), email); + } + + @Override + public TreeNode createClassAnnotatorTree(User user) { + + UserAnnotationSchema annotationSchema = prepareUserAnnotationSchema(user); + + ElementUtils elementUtils = new ElementUtils(); + + ClassTreeNode top = elementUtils.getAnnotatedElementClassTree(); + + Class<?> clazz = top.getClazz(); + TreeNode root = new DefaultTreeNode(new AnnotatedObjectTreeRow(top, modelAnnotator.getAvailableAnnotators(clazz), + modelAnnotator.getAnnotatorsFromCommonNames(annotationSchema.getAnnotatorsForClass(clazz)), + annotationSchema.getValidAnnotations(clazz), annotationSchema.getRequiredAnnotations(clazz)), null); + + root.setExpanded(true); + + Queue<Pair<ClassTreeNode, TreeNode>> nodes = new LinkedList<Pair<ClassTreeNode, TreeNode>>(); + nodes.add(new Pair<ClassTreeNode, TreeNode>(top, root)); + // create children + + Queue<TreeNode> expandParents = new LinkedList<TreeNode>(); + + while (!nodes.isEmpty()) { + Pair<ClassTreeNode, TreeNode> element = nodes.poll(); + + for (ClassTreeNode node : element.getLeft().getChildren()) { + + clazz = node.getClazz(); + AnnotatedObjectTreeRow data = new AnnotatedObjectTreeRow(node, modelAnnotator.getAvailableAnnotators(clazz), + modelAnnotator.getAnnotatorsFromCommonNames(annotationSchema.getAnnotatorsForClass(clazz)), + annotationSchema.getValidAnnotations(clazz), annotationSchema.getRequiredAnnotations(clazz)); + TreeNode treeNode = new DefaultTreeNode(data, element.getRight()); + nodes.add(new Pair<ClassTreeNode, TreeNode>(node, treeNode)); + if (data.getUsedAnnotators().size() > 0 || data.getValidAnnotators().size() > 0) { + expandParents.add(treeNode); + } + } + } + while (!expandParents.isEmpty()) { + TreeNode node = expandParents.poll(); + if (node.getParent() != null && !node.getParent().isExpanded()) { + node.getParent().setExpanded(true); + expandParents.add(node.getParent()); + } + } + + return root; + + } + + /** + * Retrieves (or creates) annotation schema for a given user. + * + * @param user + * for this users {@link UserAnnotationSchema} will be prepared + * @return {@link UserAnnotationSchema} for {@link User} + */ + public UserAnnotationSchema prepareUserAnnotationSchema(User user) { + UserAnnotationSchema annotationSchema = null; + if (user != null) { + annotationSchema = userDao.getById(user.getId()).getAnnotationSchema(); + } + if (annotationSchema == null) { + annotationSchema = new UserAnnotationSchema(); + + ElementUtils elementUtils = new ElementUtils(); + + ClassTreeNode top = elementUtils.getAnnotatedElementClassTree(); + + Map<Class<? extends BioEntity>, Set<MiriamType>> validMiriam = modelAnnotator.getDefaultValidClasses(); + Map<Class<? extends BioEntity>, Set<MiriamType>> requiredMiriam = modelAnnotator.getDefaultRequiredClasses(); + + Queue<ClassTreeNode> nodes = new LinkedList<ClassTreeNode>(); + nodes.add(top); + + while (!nodes.isEmpty()) { + ClassTreeNode element = nodes.poll(); + annotationSchema.addClassAnnotator(new UserClassAnnotators(element.getClazz(), + modelAnnotator.getAvailableDefaultAnnotatorNames(element.getClazz()))); + annotationSchema.addClassValidAnnotations( + new UserClassValidAnnotations(element.getClazz(), validMiriam.get(element.getClazz()))); + annotationSchema.addClassRequiredAnnotations( + new UserClassRequiredAnnotations(element.getClazz(), requiredMiriam.get(element.getClazz()))); + for (ClassTreeNode node : element.getChildren()) { + nodes.add(node); + } + } + User dbUser = userDao.getById(user.getId()); + dbUser.setAnnotationSchema(annotationSchema); + userDao.update(dbUser); + } + return annotationSchema; + } + + @Override + public void updateClassAnnotatorTreeForUser(User user, TreeNode annotatorsTree, boolean sbgnFormat, + boolean networkLayoutAsDefault) { + User dbUser = userDao.getById(user.getId()); + if (dbUser.getAnnotationSchema() == null) { + dbUser.setAnnotationSchema(new UserAnnotationSchema()); + } + UserAnnotationSchema annotationSchema = dbUser.getAnnotationSchema(); + + Queue<TreeNode> queue = new LinkedList<TreeNode>(); + queue.add(annotatorsTree); + while (!queue.isEmpty()) { + TreeNode node = queue.poll(); + for (TreeNode child : node.getChildren()) { + queue.add(child); + } + AnnotatedObjectTreeRow data = (AnnotatedObjectTreeRow) node.getData(); + annotationSchema.addClassAnnotator(new UserClassAnnotators(data.getClazz(), data.getUsedAnnotators())); + annotationSchema.addClassRequiredAnnotations( + new UserClassRequiredAnnotations(data.getClazz(), data.getRequiredAnnotations())); + annotationSchema + .addClassValidAnnotations(new UserClassValidAnnotations(data.getClazz(), data.getValidAnnotations())); + } + annotationSchema.setSbgnFormat(sbgnFormat); + annotationSchema.setNetworkLayoutAsDefault(networkLayoutAsDefault); + userService.updateUser(dbUser); + user.setAnnotationSchema(annotationSchema); + } + + /** + * Sends email about unsuccessfull project creation. + * + * @param projectName + * name of the project + * @param email + * email where we want to send information + * @param e + * exception that caused problem + */ + private void sendUnsuccesfullEmail(String projectName, String email, Exception e) { + EmailSender emailSender = new EmailSender(configurationService); + StringBuilder content = new StringBuilder(""); + content.append("There was a problem with generating " + projectName + " map.<br/>"); + content.append(e.getClass().getName() + ": " + e.getMessage()); + try { + emailSender.sendEmail("MapViewer notification", content.toString(), email); + } catch (MessagingException e1) { + logger.error(e1); + } + } + + /** + * Sends email about successfull project creation. + * + * @param originalModel + * model that was created + * @throws MessagingException + * exception thrown when there is a problem with sending email + */ + protected void sendSuccesfullEmail(Model originalModel) throws MessagingException { + EmailSender emailSender = new EmailSender(configurationService); + StringBuilder content = new StringBuilder("Your map was generated successfully.<br/>"); + emailSender.sendEmail("MapViewer notification", content.toString(), originalModel.getProject().getNotifyEmail()); + } + + @Override + public void updateProject(Project project) { + projectDao.update(project); + } + + /** + * This method handles situation when sever db error appeard during uploading of + * the project into database. + * + * @param params + * parameters used to create project + * @param e + * exception that occured during uploading of the project + */ + private void handleHibernateExceptionReporting(CreateProjectParams params, HibernateException e) { + // we need to open separate thread because current one thrown db exception + // and transaction is corrupetd and will be rolledback + Thread reportInSeparateThread = new Thread(new Runnable() { + + @Override + public void run() { + dbUtils.createSessionForCurrentThread(); + + try { + Project project = getProjectByProjectId(params.getProjectId(), params.getAuthenticationToken()); + String errorMessage = "Problem with uploading to database. " + + "You might violated some unhandled constraints or you run out of memory. Underlaying eror:\n" + + e.getMessage() + "\nMore information can be found in log file."; + project.setErrors(errorMessage); + project.setStatus(ProjectStatus.FAIL); + projectDao.update(project); + } catch (Exception e) { + logger.error(e, e); + } finally { + dbUtils.closeSessionForCurrentThread(); + } + } + + }); + reportInSeparateThread.start(); + + } + + @Override + public byte[] getInputDataForProject(ProjectView projectView) { + return getInputDataForProject(projectView.getIdObject()); + } + + /** + * Returns byte array containing data from original input file that was used to + * create this project. + * + * @param projectId + * identifier of project for which we want to retrieve original file + * data + * @return original data file for given layout, if such file is not stored in + * database (compatibility reasons) then null is returned + */ + private byte[] getInputDataForProject(int projectId) { + Project project = projectDao.getById(projectId); + if (project == null || project.getInputData() == null) { + return null; + } else { + return project.getInputData().getFileContent(); + } + } + + /** + * Method that handles exception reporting during creation of a project. + * + * @param params + * set of parameters used to create project + * @param e + * exception that caused problems + */ + private void handleCreateProjectException(final CreateProjectParams params, Exception e) { + Project p = projectDao.getProjectByProjectId(params.getProjectId()); + logger.error(e.getMessage(), e); + if (p != null) { + p.setErrors("Problem with uploading map: " + e.getMessage() + ". More details can be found in log file."); + } + updateProjectStatus(p, ProjectStatus.FAIL, IProgressUpdater.MAX_PROGRESS, params); + + String email = params.getNotifyEmail(); + String projectName = params.getProjectId(); + + LogParams logParams = new LogParams().description("Failed: " + e.getMessage()).type(LogType.MAP_CREATED).object(p); + logService.log(logParams); + if (email != null) { + sendUnsuccesfullEmail(projectName, email, e); + } + } + + /** + * @return the taxonomyBackend + * @see #taxonomyBackend + */ + public TaxonomyBackend getTaxonomyBackend() { + return taxonomyBackend; + } + + /** + * @param taxonomyBackend + * the taxonomyBackend to set + * @see #taxonomyBackend + */ + public void setTaxonomyBackend(TaxonomyBackend taxonomyBackend) { + this.taxonomyBackend = taxonomyBackend; + } } -- GitLab