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
+
+
+
+
+
+
+
+
+