From e4ecaceb31e2e89d06c48f24e4c59a0c702b8ffe Mon Sep 17 00:00:00 2001 From: Eike Cochu <eike@cochu.com> Date: Sun, 20 Dec 2015 18:14:17 +0100 Subject: [PATCH] generalized util services fixed import option exception structure --- .../main/java/de/vipra/cmd/CmdOptions.java | 9 +-- .../src/main/java/de/vipra/cmd/Main.java | 30 ++++--- .../java/de/vipra/cmd/option/Command.java | 9 +++ .../{ImportOption.java => ImportCommand.java} | 79 ++++++++----------- .../vipra/rest/resource/ArticleResource.java | 45 +++++++---- .../de/vipra/rest/service/ArticleService.java | 5 +- .../de/vipra/util/ex/DatabaseException.java | 15 ++++ .../de/vipra/util/ex/FilebaseException.java | 15 ++++ .../vipra/util/service/DatabaseService.java | 30 +++++-- .../vipra/util/service/FilebaseService.java | 47 +++++++++-- .../java/de/vipra/util/service/Service.java | 15 ++++ 11 files changed, 204 insertions(+), 95 deletions(-) create mode 100644 vipra-cmd/src/main/java/de/vipra/cmd/option/Command.java rename vipra-cmd/src/main/java/de/vipra/cmd/option/{ImportOption.java => ImportCommand.java} (62%) create mode 100644 vipra-util/src/main/java/de/vipra/util/ex/DatabaseException.java create mode 100644 vipra-util/src/main/java/de/vipra/util/ex/FilebaseException.java create mode 100644 vipra-util/src/main/java/de/vipra/util/service/Service.java diff --git a/vipra-cmd/src/main/java/de/vipra/cmd/CmdOptions.java b/vipra-cmd/src/main/java/de/vipra/cmd/CmdOptions.java index cf60b706..6cfd7f0a 100644 --- a/vipra-cmd/src/main/java/de/vipra/cmd/CmdOptions.java +++ b/vipra-cmd/src/main/java/de/vipra/cmd/CmdOptions.java @@ -2,7 +2,6 @@ package de.vipra.cmd; import org.apache.commons.cli.HelpFormatter; import org.apache.commons.cli.Option; -import org.apache.commons.cli.OptionGroup; import org.apache.commons.cli.Options; public class CmdOptions extends Options { @@ -10,6 +9,7 @@ public class CmdOptions extends Options { private static final long serialVersionUID = 1L; public static final String OPT_HELP = "h"; + public static final String OPT_HELP_LONG = "help"; public static final String OPT_IMPORT = "i"; public static final String OPT_SHELL = "x"; @@ -17,14 +17,9 @@ public class CmdOptions extends Options { addOption(Option.builder(OPT_HELP).longOpt("help").desc("print this message").build()); addOption(Option.builder(OPT_SHELL).longOpt("shell").hasArg(true).argName("name") .desc("run from a shell script").build()); - - OptionGroup opts = new OptionGroup(); - opts.setRequired(true); - - opts.addOption(Option.builder(OPT_IMPORT).longOpt("import").hasArgs().argName("files/dirs...") + addOption(Option.builder(OPT_IMPORT).longOpt("import").hasArgs().argName("files/dirs...") .desc("import articles into the database").build()); - addOptionGroup(opts); } public void printHelp(String cmd) { diff --git a/vipra-cmd/src/main/java/de/vipra/cmd/Main.java b/vipra-cmd/src/main/java/de/vipra/cmd/Main.java index 152984f9..9ffcc8bf 100644 --- a/vipra-cmd/src/main/java/de/vipra/cmd/Main.java +++ b/vipra-cmd/src/main/java/de/vipra/cmd/Main.java @@ -4,8 +4,6 @@ import static de.vipra.cmd.CmdOptions.OPT_HELP; import static de.vipra.cmd.CmdOptions.OPT_IMPORT; import static de.vipra.cmd.CmdOptions.OPT_SHELL; -import java.io.IOException; - import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.CommandLineParser; import org.apache.commons.cli.DefaultParser; @@ -13,19 +11,27 @@ import org.apache.commons.cli.ParseException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import de.vipra.cmd.option.ImportOption; -import de.vipra.util.ConfigException; +import de.vipra.cmd.option.Command; +import de.vipra.cmd.option.ImportCommand; public class Main { public static final Logger log = LoggerFactory.getLogger(Main.class); - public static void main(String[] args) throws IOException, ConfigException { + public static void main(String[] args) { CommandLineParser parser = new DefaultParser(); CmdOptions options = new CmdOptions(); String cmd = "vipra-cmd.jar"; + CommandLine cline; + try { + cline = parser.parse(options, args); + } catch (ParseException e) { + log.error(e.getMessage()); + options.printHelp(cmd); + return; + } + try { - CommandLine cline = parser.parse(options, args); if (cline.hasOption(OPT_SHELL)) { cmd = cline.getOptionValue(OPT_SHELL); if (cmd == null) { @@ -33,18 +39,20 @@ public class Main { } } + Command c = null; + if (cline.hasOption(OPT_HELP)) { options.printHelp(cmd); } else if (cline.hasOption(OPT_IMPORT)) { String[] paths = cline.getOptionValues(OPT_IMPORT); - ImportOption opt = new ImportOption(paths); - opt.doImport(); + c = new ImportCommand(paths); + } + + if (c != null) { + c.run(); } else { options.printHelp(cmd); } - } catch (ParseException e) { - log.error(e.getMessage()); - options.printHelp(cmd); } catch (ExecutionException e) { log.error(e.getMessage()); } diff --git a/vipra-cmd/src/main/java/de/vipra/cmd/option/Command.java b/vipra-cmd/src/main/java/de/vipra/cmd/option/Command.java new file mode 100644 index 00000000..5ad51101 --- /dev/null +++ b/vipra-cmd/src/main/java/de/vipra/cmd/option/Command.java @@ -0,0 +1,9 @@ +package de.vipra.cmd.option; + +import de.vipra.cmd.ExecutionException; + +public interface Command { + + public void run() throws ExecutionException; + +} diff --git a/vipra-cmd/src/main/java/de/vipra/cmd/option/ImportOption.java b/vipra-cmd/src/main/java/de/vipra/cmd/option/ImportCommand.java similarity index 62% rename from vipra-cmd/src/main/java/de/vipra/cmd/option/ImportOption.java rename to vipra-cmd/src/main/java/de/vipra/cmd/option/ImportCommand.java index c7eb0546..807335a3 100644 --- a/vipra-cmd/src/main/java/de/vipra/cmd/option/ImportOption.java +++ b/vipra-cmd/src/main/java/de/vipra/cmd/option/ImportCommand.java @@ -7,17 +7,12 @@ import java.io.IOException; import java.util.ArrayList; import java.util.List; -import org.bson.Document; import org.json.simple.JSONArray; import org.json.simple.JSONObject; import org.json.simple.parser.JSONParser; -import org.json.simple.parser.ParseException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.mongodb.client.MongoCollection; -import com.mongodb.client.MongoDatabase; - import de.vipra.cmd.ExecutionException; import de.vipra.cmd.model.Article; import de.vipra.util.Config; @@ -25,18 +20,23 @@ import de.vipra.util.ConfigException; import de.vipra.util.Constants; import de.vipra.util.Mongo; import de.vipra.util.StringUtils; +import de.vipra.util.ex.DatabaseException; +import de.vipra.util.ex.FilebaseException; +import de.vipra.util.service.DatabaseService; +import de.vipra.util.service.FilebaseService; -public class ImportOption { +public class ImportCommand implements Command { - public static final Logger log = LoggerFactory.getLogger(ImportOption.class); + public static final Logger log = LoggerFactory.getLogger(ImportCommand.class); public static final Logger out = LoggerFactory.getLogger("shellout"); private ArrayList<File> files = new ArrayList<>(); private JSONParser parser = new JSONParser(); private Config config; - private MongoCollection<Document> collection; + private DatabaseService<Article> dbArticles; + private FilebaseService<Article> fbArticles; - public ImportOption(String[] paths) throws ExecutionException { + public ImportCommand(String[] paths) throws ExecutionException { addPaths(paths); } @@ -61,42 +61,21 @@ public class ImportOption { files.add(file); } else if (file.isDirectory()) { File[] files = file.listFiles(new FilenameFilter() { - @Override public boolean accept(File dir, String name) { return dir.isFile(); } - }); addPaths(files); } } - private void importFile(File file) throws ExecutionException { - Object data; - try { - data = parser.parse(new FileReader(file)); - } catch (IOException e) { - throw new ExecutionException("file not found: " + file.getAbsolutePath()); - } catch (ParseException e) { - throw new ExecutionException("could not parse json in file: " + file.getAbsolutePath()); - } + private void importFile(File file) throws Exception { + Object data = parser.parse(new FileReader(file)); try { - JSONArray array = (JSONArray) data; - for (Object object : array) { - List<Exception> errors = new ArrayList<>(); - try { - importArticle((JSONObject) object); - } catch (ExecutionException e) { - errors.add(e); - } - if (errors.size() > 0) { - throw new ExecutionException(errors); - } - } - return; + importArticles((JSONArray) data); } catch (ClassCastException e) { try { importArticle((JSONObject) data); @@ -106,8 +85,21 @@ public class ImportOption { } } - private void importArticle(JSONObject obj) throws ExecutionException { - // 2. add article to file database + private void importArticles(JSONArray array) throws ExecutionException { + List<Exception> errors = new ArrayList<>(); + for (Object object : array) { + try { + importArticle((JSONObject) object); + } catch (Exception e) { + errors.add(e); + } + } + if (errors.size() > 0) { + throw new ExecutionException(errors); + } + } + + private void importArticle(JSONObject obj) throws FilebaseException, DatabaseException { // 4. topic modeling // 3. index article via elasticsearch, include topics out.info("importing \"" + StringUtils.ellipsize(obj.get("title").toString(), 80) + "\""); @@ -115,21 +107,20 @@ public class ImportOption { article.fromJSON(obj); // add article to mongodb - Document document = article.toDocument(); - try { - collection.insertOne(document); - } catch (Exception e) { - throw new ExecutionException("could not create database entry: " + e.getMessage()); - } + article = dbArticles.createSingle(article); + // add article to filebase + article = fbArticles.createSingle(article); } - public void doImport() throws ExecutionException { + @Override + public void run() throws ExecutionException { try { config = new Config(); + File dataDir = null; Mongo mongo = Mongo.getInstance(config); - MongoDatabase db = mongo.getDatabase(); - collection = db.getCollection(Constants.Collection.ARTICLES.name); + dbArticles = new DatabaseService<Article>(mongo, Constants.Collection.ARTICLES, Article.class); + fbArticles = new FilebaseService<Article>(dataDir, Article.class); } catch (IOException | ConfigException e) { throw new ExecutionException(e); } diff --git a/vipra-rest/src/main/java/de/vipra/rest/resource/ArticleResource.java b/vipra-rest/src/main/java/de/vipra/rest/resource/ArticleResource.java index c346a083..526d14d3 100644 --- a/vipra-rest/src/main/java/de/vipra/rest/resource/ArticleResource.java +++ b/vipra-rest/src/main/java/de/vipra/rest/resource/ArticleResource.java @@ -27,6 +27,7 @@ import de.vipra.rest.service.ArticleService; import de.vipra.util.Config; import de.vipra.util.ConfigException; import de.vipra.util.Mongo; +import de.vipra.util.ex.DatabaseException; @Path("articles") public class ArticleResource { @@ -79,9 +80,16 @@ public class ArticleResource { @Consumes(APIMediaType.APPLICATION_JSONAPI) @Produces(APIMediaType.APPLICATION_JSONAPI) public Response createArticle(Article article) { - article = service.createArticle(uri.getAbsolutePath(), article); - ResponseWrapper<Article> res = new ResponseWrapper<>(article); - return Response.created(article.uri(uri.getAbsolutePath())).entity(res).build(); + ResponseWrapper<Article> res; + try { + article = service.createArticle(uri.getAbsolutePath(), article); + res = new ResponseWrapper<>(article); + return Response.created(article.uri(uri.getAbsolutePath())).entity(res).build(); + } catch (DatabaseException e) { + res = new ResponseWrapper<>(new APIError(Response.Status.INTERNAL_SERVER_ERROR, "item could not be created", + "item could not be created due to an internal server error")); + return Response.serverError().entity(res).build(); + } } @DELETE @@ -107,20 +115,27 @@ public class ArticleResource { @Produces(APIMediaType.APPLICATION_JSONAPI) @Path("{id}") public Response updateArticle(@PathParam("id") String id, Article article) { - long updated = service.updateArticle(uri.getAbsolutePath(), article); - int upd = updated > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) updated; ResponseWrapper<Article> res = new ResponseWrapper<>(); - switch (upd) { - case 0: - res.addError(new APIError(Response.Status.NOT_FOUND, "Article not found", - String.format(Messages.NOT_FOUND, "article", id))); - return Response.status(Response.Status.NOT_FOUND).entity(res).build(); - case 1: - res.setData(article); - return Response.ok().entity(res).build(); - default: - return Response.serverError().build(); + try { + long updated = service.updateArticle(uri.getAbsolutePath(), article); + int updatedInt = updated > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) updated; + switch (updatedInt) { + case 0: + res.addError(new APIError(Response.Status.NOT_FOUND, "Article not found", + String.format(Messages.NOT_FOUND, "article", id))); + return Response.status(Response.Status.NOT_FOUND).entity(res).build(); + case 1: + res.setData(article); + return Response.ok().entity(res).build(); + default: + return Response.serverError().build(); + } + } catch (DatabaseException e) { + res = new ResponseWrapper<>(new APIError(Response.Status.INTERNAL_SERVER_ERROR, "item could not be updated", + "item could not be updated due to an internal server error")); + return Response.serverError().entity(res).build(); } + } } diff --git a/vipra-rest/src/main/java/de/vipra/rest/service/ArticleService.java b/vipra-rest/src/main/java/de/vipra/rest/service/ArticleService.java index 4f7c1e93..9f432a2a 100644 --- a/vipra-rest/src/main/java/de/vipra/rest/service/ArticleService.java +++ b/vipra-rest/src/main/java/de/vipra/rest/service/ArticleService.java @@ -6,6 +6,7 @@ import java.util.ArrayList; import de.vipra.rest.model.Article; import de.vipra.util.Constants; import de.vipra.util.Mongo; +import de.vipra.util.ex.DatabaseException; import de.vipra.util.service.DatabaseService; public class ArticleService extends DatabaseService<Article> { @@ -31,7 +32,7 @@ public class ArticleService extends DatabaseService<Article> { return articles; } - public Article createArticle(URI base, Article article) { + public Article createArticle(URI base, Article article) throws DatabaseException { article = super.createSingle(article); if (article != null) { article.setBase(base); @@ -43,7 +44,7 @@ public class ArticleService extends DatabaseService<Article> { return super.deleteSingle(id); } - public long updateArticle(URI base, Article article) { + public long updateArticle(URI base, Article article) throws DatabaseException { long updated = super.updateSingle(article); article.setBase(base); return updated; diff --git a/vipra-util/src/main/java/de/vipra/util/ex/DatabaseException.java b/vipra-util/src/main/java/de/vipra/util/ex/DatabaseException.java new file mode 100644 index 00000000..6782255b --- /dev/null +++ b/vipra-util/src/main/java/de/vipra/util/ex/DatabaseException.java @@ -0,0 +1,15 @@ +package de.vipra.util.ex; + +public class DatabaseException extends Exception { + + private static final long serialVersionUID = 1L; + + public DatabaseException(String msg) { + super(msg); + } + + public DatabaseException(Exception e) { + super(e); + } + +} diff --git a/vipra-util/src/main/java/de/vipra/util/ex/FilebaseException.java b/vipra-util/src/main/java/de/vipra/util/ex/FilebaseException.java new file mode 100644 index 00000000..99dd450e --- /dev/null +++ b/vipra-util/src/main/java/de/vipra/util/ex/FilebaseException.java @@ -0,0 +1,15 @@ +package de.vipra.util.ex; + +public class FilebaseException extends Exception { + + private static final long serialVersionUID = 1L; + + public FilebaseException(String msg) { + super(msg); + } + + public FilebaseException(Exception e) { + super(e); + } + +} diff --git a/vipra-util/src/main/java/de/vipra/util/service/DatabaseService.java b/vipra-util/src/main/java/de/vipra/util/service/DatabaseService.java index 4729cf84..9b774258 100644 --- a/vipra-util/src/main/java/de/vipra/util/service/DatabaseService.java +++ b/vipra-util/src/main/java/de/vipra/util/service/DatabaseService.java @@ -16,9 +16,10 @@ import com.mongodb.client.result.UpdateResult; import de.vipra.util.Constants; import de.vipra.util.Mongo; +import de.vipra.util.ex.DatabaseException; import de.vipra.util.model.Model; -public class DatabaseService<T extends Model> { +public class DatabaseService<T extends Model> implements Service<T, DatabaseException> { private static final Logger log = LoggerFactory.getLogger(DatabaseService.class); @@ -41,7 +42,8 @@ public class DatabaseService<T extends Model> { } } - protected T getSingle(String id) { + @Override + public T getSingle(String id) { ArrayList<Document> result = collection.find(Filters.eq("_id", objectId(id))).into(new ArrayList<Document>()); if (result.size() == 1) { return newT(result.get(0)); @@ -50,7 +52,7 @@ public class DatabaseService<T extends Model> { } } - protected ArrayList<T> getMultiple(int skip, int limit, String sortBy) { + public ArrayList<T> getMultiple(int skip, int limit, String sortBy) { ArrayList<Document> documents = collection.find().skip(skip).limit(limit).sort(getSorts(sortBy)) .into(new ArrayList<Document>()); ArrayList<T> items = new ArrayList<>(documents.size()); @@ -62,22 +64,34 @@ public class DatabaseService<T extends Model> { return items; } - protected T createSingle(T t) { + @Override + public T createSingle(T t) throws DatabaseException { Document document = new Document(t.toDocument()); - collection.insertOne(document); + try { + collection.insertOne(document); + } catch (Exception e) { + throw new DatabaseException(e); + } t.fromDocument(document); return t; } - protected long deleteSingle(String id) { + @Override + public long deleteSingle(String id) { DeleteResult result = collection.deleteOne(Filters.eq("_id", objectId(id))); return result.getDeletedCount(); } - protected long updateSingle(T t) { + @Override + public long updateSingle(T t) throws DatabaseException { Document docOld = new Document("_id", objectId(t.getId())); Document docNew = t.toDocument(); - UpdateResult result = collection.replaceOne(docOld, docNew); + UpdateResult result; + try { + result = collection.replaceOne(docOld, docNew); + } catch (Exception e) { + throw new DatabaseException(e); + } t.fromDocument(docNew); return result.getModifiedCount(); } diff --git a/vipra-util/src/main/java/de/vipra/util/service/FilebaseService.java b/vipra-util/src/main/java/de/vipra/util/service/FilebaseService.java index 3dfa8c74..e369e216 100644 --- a/vipra-util/src/main/java/de/vipra/util/service/FilebaseService.java +++ b/vipra-util/src/main/java/de/vipra/util/service/FilebaseService.java @@ -6,9 +6,10 @@ import java.io.IOException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import de.vipra.util.ex.FilebaseException; import de.vipra.util.model.Model; -public class FilebaseService<T extends Model> { +public class FilebaseService<T extends Model> implements Service<T, FilebaseException> { public static final Logger log = LoggerFactory.getLogger(FilebaseService.class); @@ -32,27 +33,57 @@ public class FilebaseService<T extends Model> { } } - protected File getFile(String id) { + public File getFile(String id) { return new File(directory, id); } - protected T getSingle(String id) { + @Override + public T getSingle(String id) { File file = getFile(id); return newT(file); } - protected long deleteSingle(String id) { + @Override + public T createSingle(T t) throws FilebaseException { + if (t.getId() != null) { + File file = getFile(t.getId()); + if (file.exists()) { + if (!file.delete()) { + log.error("could not delete file for recreation: " + file.getAbsolutePath()); + } + } + try { + t.writeToFile(file); + } catch (IOException e) { + throw new FilebaseException(e); + } + } + return t; + } + + @Override + public long deleteSingle(String id) throws FilebaseException { File file = getFile(id); - if (file.exists() && file.delete()) { - return 1; + if (file.exists()) { + if (file.delete()) { + return 1; + } else { + throw new FilebaseException("could not delete file: " + file.getAbsolutePath()); + } } return 0; } - protected long updateSingle(T t) throws IOException { + @Override + public long updateSingle(T t) throws FilebaseException { File file = getFile(t.getId()); if (file.exists()) { - t.writeToFile(file); + try { + t.writeToFile(file); + return 1; + } catch (Exception e) { + throw new FilebaseException(e); + } } return 0; } diff --git a/vipra-util/src/main/java/de/vipra/util/service/Service.java b/vipra-util/src/main/java/de/vipra/util/service/Service.java new file mode 100644 index 00000000..824a6a97 --- /dev/null +++ b/vipra-util/src/main/java/de/vipra/util/service/Service.java @@ -0,0 +1,15 @@ +package de.vipra.util.service; + +import de.vipra.util.model.Model; + +public interface Service<T extends Model, E extends Exception> { + + T getSingle(String id) throws E; + + T createSingle(T t) throws E; + + long deleteSingle(String id) throws E; + + long updateSingle(T t) throws E; + +} -- GitLab