diff --git a/pom.xml b/pom.xml index ec0b6a33..703dc3ee 100644 --- a/pom.xml +++ b/pom.xml @@ -238,11 +238,25 @@ slf4j-api 1.7.21 + + + ch.qos.logback + logback-classic + 1.1.8 + + + ch.qos.logback + logback-core + 1.1.8 + + + @@ -389,10 +403,17 @@ net.kencochrane.raven - raven-log4j + raven-logback 6.0.0 + org.apache.lucene @@ -439,12 +460,19 @@ ${metrics-version} + + io.dropwizard.metrics + metrics-logback + 3.1.2 + + + io.dropwizard.metrics metrics-servlet diff --git a/src/main/java/com/parallax/server/blocklyprop/config/DaoModule.java b/src/main/java/com/parallax/server/blocklyprop/config/DaoModule.java index 6d6bd6ad..bd1f3dde 100644 --- a/src/main/java/com/parallax/server/blocklyprop/config/DaoModule.java +++ b/src/main/java/com/parallax/server/blocklyprop/config/DaoModule.java @@ -15,9 +15,26 @@ import com.parallax.server.blocklyprop.db.dao.impl.SessionDaoImpl; import com.parallax.server.blocklyprop.db.dao.impl.UserDaoImpl; + /** * * @author Michel + * + * AbstractModule: + * A support class for Modules which reduces repetition and results in a more + * readable configuration. Simply extend this class, implement configure(), + * and call the inherited methods which mirror those found in Binder. + * For example: + * + * public class MyModule extends AbstractModule { + * protected void configure() { + * bind(Service.class).to(ServiceImpl.class).in(Singleton.class); + * bind(CreditCardPaymentService.class); + * bind(PaymentService.class).to(CreditCardPaymentService.class); + * bindConstant().annotatedWith(Names.named("port")).to(8080); + * } + * } + * */ public class DaoModule extends AbstractModule { diff --git a/src/main/java/com/parallax/server/blocklyprop/config/PersistenceModule.java b/src/main/java/com/parallax/server/blocklyprop/config/PersistenceModule.java index 5fae250d..5311fe62 100644 --- a/src/main/java/com/parallax/server/blocklyprop/config/PersistenceModule.java +++ b/src/main/java/com/parallax/server/blocklyprop/config/PersistenceModule.java @@ -10,12 +10,13 @@ import com.google.inject.Provides; import com.parallax.server.blocklyprop.db.utils.DataSourceSetup; import java.sql.SQLException; -import java.util.logging.Level; -import java.util.logging.Logger; import javax.sql.DataSource; import org.apache.commons.configuration.Configuration; import org.apache.commons.dbcp2.PoolingDataSource; import org.jooq.SQLDialect; +import java.util.logging.Level; +import java.util.logging.Logger; + /** * @@ -37,6 +38,7 @@ protected void configure() { @Provides PoolingDataSource dataSource() throws ClassNotFoundException { + PoolingDataSource ds = DataSourceSetup.connect(configuration); try { ds.getConnection(); diff --git a/src/main/java/com/parallax/server/blocklyprop/config/SetupConfig.java b/src/main/java/com/parallax/server/blocklyprop/config/SetupConfig.java index b305c0f5..f442be35 100644 --- a/src/main/java/com/parallax/server/blocklyprop/config/SetupConfig.java +++ b/src/main/java/com/parallax/server/blocklyprop/config/SetupConfig.java @@ -21,6 +21,10 @@ import org.apache.commons.configuration.Configuration; import org.apache.commons.configuration.ConfigurationException; import org.apache.commons.configuration.DefaultConfigurationBuilder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import ch.qos.logback.classic.LoggerContext; + /** * @@ -29,6 +33,7 @@ public class SetupConfig extends GuiceServletContextListener { private Configuration configuration; + private final Logger LOG = LoggerFactory.getLogger(this.getClass()); @Override protected Injector getInjector() { @@ -62,10 +67,11 @@ protected void configure() { private void readConfiguration() { try { - System.out.println("Looking for blocklyprop.properties in: " + System.getProperty("user.home")); + LOG.info("Looking for blocklyprop.properties in: {}", System.getProperty("user.home")); DefaultConfigurationBuilder configurationBuilder = new DefaultConfigurationBuilder(getClass().getResource("/config.xml")); configuration = configurationBuilder.getConfiguration(); } catch (ConfigurationException ce) { + LOG.error("{}", ce.getMessage()); ce.printStackTrace(); } catch (Throwable t) { t.printStackTrace(); @@ -81,12 +87,20 @@ public void contextDestroyed(ServletContextEvent servletContextEvent) { Driver driver = drivers.nextElement(); try { DriverManager.deregisterDriver(driver); - // LOG.log(Level.INFO, String.format("deregistering jdbc driver: %s", driver)); + LOG.info("deregistering jdbc driver: {}",driver); } catch (SQLException sqlE) { // LOG.log(Level.SEVERE, String.format("Error deregistering driver %s", driver), e); } } + + // Shut down the loggers. Assume SLF4J is bound to logback-classic + // in the current environment + LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory(); + if (loggerContext != null) { + loggerContext.stop(); + } + } } diff --git a/src/main/java/com/parallax/server/blocklyprop/db/dao/impl/ProjectDaoImpl.java b/src/main/java/com/parallax/server/blocklyprop/db/dao/impl/ProjectDaoImpl.java index b897363e..5ffe50a8 100644 --- a/src/main/java/com/parallax/server/blocklyprop/db/dao/impl/ProjectDaoImpl.java +++ b/src/main/java/com/parallax/server/blocklyprop/db/dao/impl/ProjectDaoImpl.java @@ -20,14 +20,18 @@ import org.jooq.Condition; import org.jooq.DSLContext; import org.jooq.SortField; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * * @author Michel + * + * TODO: add details. */ @Singleton public class ProjectDaoImpl implements ProjectDao { - + private static final Logger LOG = LoggerFactory.getLogger(ProjectDao.class); private DSLContext create; @Inject @@ -35,137 +39,232 @@ public void setDSLContext(DSLContext dsl) { this.create = dsl; } - // Swap out old block definitions - private ProjectRecord alterReadRecord(ProjectRecord record) { - String newCode; - - if (record == null) { - throw new NullPointerException("Cannot alter a null project record."); - } - - try { - newCode = record.getCode(); - - // Return immediately if there is no code to adjust - if (newCode == null) { - return record; - } - - if (record.getType() == ProjectType.SPIN) { - newCode = newCode.replaceAll("block type=\"controls_if\"", "block type=\"controls_boolean_if\""); - newCode = newCode.replaceAll("block type=\"logic_compare\"", "block type=\"logic_boolean_compare\""); - newCode = newCode.replaceAll("block type=\"logic_operation\"", "block type=\"logic_boolean_operation\""); - newCode = newCode.replaceAll("block type=\"logic_negate\"", "block type=\"logic_boolean_negate\""); - newCode = newCode.replaceAll("block type=\"math_number\"", "block type=\"spin_integer\""); - } else if (record.getType() == ProjectType.PROPC){ - newCode = newCode.replaceAll("field name=\"OP\">ADD + MINUS - MULTIPLY * DIVIDE / MODULUS % AND && AND_NOT && !LT<GT>LTE<=GTE>=EQ==NEQ!=INCHES_inchesCM_cm getUserProjects(Long idUser, TableSort sort, TableOrder order, Integer limit, Integer offset) { + LOG.info("Retreive projects for user {}.", idUser); + SortField orderField = Tables.PROJECT.NAME.asc(); if (TableOrder.desc == order) { orderField = Tables.PROJECT.NAME.desc(); } - return create.selectFrom(Tables.PROJECT).where(Tables.PROJECT.ID_USER.equal(idUser)).orderBy(orderField).limit(limit).offset(offset).fetch(); + + return create.selectFrom(Tables.PROJECT) + .where(Tables.PROJECT.ID_USER.equal(idUser)) + .orderBy(orderField).limit(limit).offset(offset) + .fetch(); } + /** + * TODO: add details. + * + * @param sort + * @param order + * @param limit + * @param offset + * @param idUser + * @return + */ @Override public List getSharedProjects(TableSort sort, TableOrder order, Integer limit, Integer offset, Long idUser) { + LOG.info("Retreive shared projects."); + SortField orderField = sort == null ? Tables.PROJECT.NAME.asc() : sort.getField().asc(); if (TableOrder.desc == order) { orderField = sort == null ? Tables.PROJECT.NAME.desc() : sort.getField().desc(); @@ -215,16 +355,36 @@ public List getSharedProjects(TableSort sort, TableOrder order, I if (idUser != null) { conditions = conditions.and(Tables.PROJECT.ID_USER.eq(idUser)); } - return create.selectFrom(Tables.PROJECT).where(conditions).orderBy(orderField).limit(limit).offset(offset).fetch(); + return create.selectFrom(Tables.PROJECT) + .where(conditions) + .orderBy(orderField).limit(limit).offset(offset) + .fetch(); } + /** + * TODO: add details. + * + * @param idUser + * @return + */ @Override public int countUserProjects(Long idUser) { + LOG.info("Count project for user {}.", idUser); + return create.fetchCount(Tables.PROJECT, Tables.PROJECT.ID_USER.equal(idUser)); } + /** + * + * TODO: add details. + * + * @param idUser + * @return + */ @Override public int countSharedProjects(Long idUser) { + LOG.info("Count shared projects for user {}.", idUser); + Condition conditions = Tables.PROJECT.SHARED.equal(Boolean.TRUE); if (idUser != null) { conditions = conditions.and(Tables.PROJECT.ID_USER.eq(idUser)); @@ -232,8 +392,16 @@ public int countSharedProjects(Long idUser) { return create.fetchCount(Tables.PROJECT, conditions); } + /** + * TODO: add details. + * + * @param idProject + * @return + */ @Override public ProjectRecord cloneProject(Long idProject) { + LOG.info("Clone existing project {} to a new project.", idProject); + ProjectRecord original = getProject(idProject); if (original == null) { throw new NullPointerException("Project doesn't exist"); @@ -245,35 +413,30 @@ public ProjectRecord cloneProject(Long idProject) { return null; } - private ProjectRecord doProjectClone(ProjectRecord original) { - ProjectRecord cloned = createProject( - original.getName(), - original.getDescription(), - original.getDescriptionHtml(), - original.getCode(), - original.getType(), - original.getBoard(), - original.getPrivate(), - original.getShared()); - - cloned.setBasedOn(original.getId()); - cloned.update(); - - create.update(Tables.PROJECT) - .set(Tables.PROJECT.BASED_ON, original.getId()) - .where(Tables.PROJECT.ID.equal(cloned.getId())); - return cloned; - } - + /** + * TODO: add details. + * + * @param idProject + * @return + */ @Override public boolean deleteProject(Long idProject) { + LOG.info("Delete project {}.", idProject); return create.deleteFrom(Tables.PROJECT) .where(Tables.PROJECT.ID.equal(idProject)) .execute() > 0; } + /** + * TODO: add details. + * + * @param idProject + * @param code + * @return + */ @Override public ProjectRecord updateProjectCode(Long idProject, String code) { + LOG.info("Update code for project {}.", idProject); ProjectRecord record = create.selectFrom(Tables.PROJECT) .where(Tables.PROJECT.ID.equal(idProject)) .fetchOne(); @@ -296,19 +459,33 @@ public ProjectRecord updateProjectCode(Long idProject, String code) { cloned.update(); return cloned; } + LOG.error("User {} tried and failed to update project {}.", idUser, idProject); throw new UnauthorizedException(); } } else { + LOG.warn("Unable to project {}. Unknown reason.", idProject); return null; } } + /** + * TODO: add details. + * + * @param idProject + * @param code + * @param newName + * @return + */ @Override public ProjectRecord saveProjectCodeAs(Long idProject, String code, String newName) { + LOG.info("Saving project code as '{}'", newName); + ProjectRecord original = getProject(idProject); if (original == null) { + LOG.error("Original project {} is missing. Unable to save code as...", idProject); throw new NullPointerException("Project doesn't exist"); } + Long idUser = BlocklyPropSecurityUtils.getCurrentUserId(); if (original.getIdUser().equals(idUser) || original.getShared()) { // TODO check if friends ProjectRecord cloned = createProject( @@ -327,4 +504,182 @@ public ProjectRecord saveProjectCodeAs(Long idProject, String code, String newNa return null; } + + private ProjectRecord getProject(Long idProject, boolean toEdit) { + LOG.info("Retreiving project {}.", idProject); + ProjectRecord record = create + .selectFrom(Tables.PROJECT) + .where(Tables.PROJECT.ID.equal(idProject)) + .fetchOne(); + + if (record != null) { + Long idUser = BlocklyPropSecurityUtils.getCurrentUserId(); + + // Return a project if the edit flag is off or the edit flag is + // on and the project owner is the current user + if (!toEdit || record.getIdUser().equals(idUser)) { + return alterReadRecord(record); + } else { + LOG.error("User {} attempted to edit project {} without authorization.", + idUser, idProject); + throw new UnauthorizedException(); + } + } + + // Return the project after checking if for depricated blocks + return alterReadRecord(record); + } + + private ProjectRecord doProjectClone(ProjectRecord original) { + ProjectRecord cloned = createProject( + original.getName(), + original.getDescription(), + original.getDescriptionHtml(), + original.getCode(), + original.getType(), + original.getBoard(), + original.getPrivate(), + original.getShared()); + + cloned.setBasedOn(original.getId()); + cloned.update(); + + create.update(Tables.PROJECT) + .set(Tables.PROJECT.BASED_ON, original.getId()) + .where(Tables.PROJECT.ID.equal(cloned.getId())); + return cloned; + } + + + // Swap out old block definitions + private ProjectRecord alterReadRecord(ProjectRecord record) { + LOG.info("Verify project block characteristics"); + String currentCode, newCode; + + + if (record == null) { + LOG.error("Null project record detected."); + throw new NullPointerException("Cannot alter a null project record."); + } + + try { + currentCode = record.getCode(); + + // Return immediately if there is no code to adjust + if (currentCode == null) { + LOG.warn("Project is empty."); + return record; + } + + /* + * Make a copy of the project. We will use this after the updates + * to determine if anything was changed. This ensures that we do + * not do any database I/O unless we actually changed something. + */ + newCode = currentCode; + + if (record.getType() == ProjectType.SPIN) { + newCode = fixSpinProjectBlocks(newCode); + + } else if (record.getType() == ProjectType.PROPC){ + newCode = fixSpinProjectBlocks(newCode); + } + + // Check for any difference from the original code + if (! currentCode.equals(newCode)) { + LOG.info("Updating converted project."); + record.setCode(newCode); + } + } + + catch (Exception ex) { + LOG.error("Exception trapped. Messate is: {}", ex.getMessage()); + } + + return record; + } + + // Correct depricated block details related to Spin blocks + private String fixSpinProjectBlocks(String newCode) { + LOG.info("Looking for depricated Spin blocks."); + + newCode = newCode.replaceAll("block type=\"controls_if\"", + "block type=\"controls_boolean_if\""); + + newCode = newCode.replaceAll("block type=\"logic_compare\"", + "block type=\"logic_boolean_compare\""); + + newCode = newCode.replaceAll("block type=\"logic_operation\"", + "block type=\"logic_boolean_operation\""); + + newCode = newCode.replaceAll("block type=\"logic_negate\"", + "block type=\"logic_boolean_negate\""); + + newCode = newCode.replaceAll("block type=\"math_number\"", + "block type=\"spin_integer\""); + return newCode; + } + + private String fixPropcProjectBlocks(String newCode) { + LOG.info("Looking for depricated PropC blocks."); + + newCode = newCode.replaceAll("field name=\"OP\">ADD + MINUS - MULTIPLY * DIVIDE / MODULUS % AND && AND_NOT && !LT<GT>LTE<=GTE>=EQ==NEQ!=INCHES_inchesCM_cm + + + + + + + + + + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + ${APP_LOG_PATH}/${APP_LOG_FILE_BASE}.log + + %date %level [%thread] %logger{10} [%file:%line] %msg%n + + + + + + + + + + + + + + + + diff --git a/src/test/resources/logback-test.xml b/src/test/resources/logback-test.xml new file mode 100644 index 00000000..9ab4db8a --- /dev/null +++ b/src/test/resources/logback-test.xml @@ -0,0 +1,25 @@ + + + + + + + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + + + +