#1145 - Refactor @ChangeLog based on JSON documents for 'new values' and 'old values'

This commit is contained in:
rob bygrave
2017-10-03 01:31:37 +13:00
parent daf3d97266
commit 368d22baa6
35 changed files with 743 additions and 503 deletions
@@ -0,0 +1,22 @@
package io.ebean.bean;
/**
* Visitor for collecting new/old values for a bean update.
*/
public interface BeanDiffVisitor {
/**
* Collect a new/old value pair.
*/
void visit(int position, Object newVal, Object oldVal);
/**
* Start processing an associated bean.
*/
void visitPush(int position);
/**
* Stop processing an associated bean.
*/
void visitPop();
}
@@ -694,6 +694,28 @@ public final class EntityBeanIntercept implements Serializable {
}
}
/**
* Recursively add dirty properties.
*/
public void addDirtyPropertyValues(BeanDiffVisitor visitor) {
int len = getPropertyLength();
for (int i = 0; i < len; i++) {
if (changedProps != null && changedProps[i]) {
// the property has been changed on this bean
Object newVal = owner._ebean_getField(i);
Object oldVal = getOrigValue(i);
visitor.visit(i, newVal, oldVal);
} else if (embeddedDirty != null && embeddedDirty[i]) {
// an embedded property has been changed - recurse
EntityBean embeddedBean = (EntityBean) owner._ebean_getField(i);
visitor.visitPush(i);
embeddedBean._ebean_getIntercept().addDirtyPropertyValues(visitor);
visitor.visitPop();
}
}
}
/**
* Return a dirty property hash taking into account embedded beans.
*/
@@ -1,9 +1,5 @@
package io.ebean.event.changelog;
import io.ebean.ValuePair;
import java.util.Map;
/**
* A bean insert, update or delete change sent as part of a ChangeSet.
*/
@@ -12,68 +8,68 @@ public class BeanChange {
/**
* The underling base table name.
*/
String table;
private final String type;
/**
* The tenantId value.
*/
Object tenantId;
private final Object tenantId;
/**
* The id value.
*/
Object id;
private final Object id;
/**
* The INSERT, UPDATE or DELETE change type.
*/
ChangeType type;
private final ChangeType event;
/**
* The time the bean change was created.
*/
long eventTime;
private final long eventTime;
/**
* The values for insert or update. Note that null values are not included for insert.
* The change in JSON form.
*/
Map<String, ValuePair> values;
private final String data;
/**
* Construct with all the details.
* The change in JSON form.
*/
public BeanChange(String table, Object tenantId, Object id, ChangeType type, Map<String, ValuePair> values) {
this.table = table;
private final String oldData;
/**
* Construct with change as JSON.
*/
public BeanChange(String type, Object tenantId, Object id, ChangeType event, String data, String oldData) {
this.type = type;
this.tenantId = tenantId;
this.id = id;
this.type = type;
this.event = event;
this.eventTime = System.currentTimeMillis();
this.values = values;
this.data = data;
this.oldData = oldData;
}
/**
* Default constructor for JSON tools.
* Construct with change as JSON.
*/
public BeanChange() {
public BeanChange(String table, Object tenantId, Object id, ChangeType event, String data) {
this(table, tenantId , id , event , data , null);
}
@Override
public String toString() {
return "table:" + table + " tenantId: " + tenantId + " id:" + id + " values:" + values;
return "type:" + type + " tenantId: " + tenantId + " id:" + id + " data:" + data;
}
/**
* Return the object type (typically table name).
*/
public String getTable() {
return table;
}
/**
* Set the object type (for JSON tools).
*/
public void setTable(String table) {
this.table = table;
public String getType() {
return type;
}
/**
@@ -83,13 +79,6 @@ public class BeanChange {
return tenantId;
}
/**
* Set the bean id (for JSON tools).
*/
public void setTenantId(Object tenantId) {
this.tenantId = tenantId;
}
/**
* Return the object id.
*/
@@ -97,25 +86,11 @@ public class BeanChange {
return id;
}
/**
* Set the bean id (for JSON tools).
*/
public void setId(Object id) {
this.id = id;
}
/**
* Return the change type (INSERT, UPDATE or DELETE).
*/
public ChangeType getType() {
return type;
}
/**
* Set the type (for JSON tools).
*/
public void setType(ChangeType type) {
this.type = type;
public ChangeType getEvent() {
return event;
}
/**
@@ -126,23 +101,16 @@ public class BeanChange {
}
/**
* Set the event time in epoch millis.
* Return the change data in JSON form.
*/
public void setEventTime(long eventTime) {
this.eventTime = eventTime;
public String getData() {
return data;
}
/**
* Return the value pairs. For inserts the ValuePair oldValue is always null.
* Return the old data in JSON form.
*/
public Map<String, ValuePair> getValues() {
return values;
}
/**
* Set the value pairs (for JSON tools).
*/
public void setValues(Map<String, ValuePair> values) {
this.values = values;
public String getOldData() {
return oldData;
}
}
@@ -25,6 +25,11 @@ import java.util.List;
*/
public interface SpiEbeanServer extends EbeanServer, BeanLoader, BeanCollectionLoader {
/**
* Return the server extended Json context.
*/
SpiJsonContext jsonExtended();
/**
* For internal use, shutdown of the server invoked by JVM Shutdown.
*/
@@ -0,0 +1,24 @@
package io.ebeaninternal.api;
import com.fasterxml.jackson.core.JsonGenerator;
import io.ebean.text.json.JsonContext;
import io.ebean.text.json.JsonWriteOptions;
import io.ebeaninternal.server.text.json.SpiJsonWriter;
import java.io.Writer;
/**
* Extended Json Context for internal server use.
*/
public interface SpiJsonContext extends JsonContext {
/**
* Create a Json Writer for writing beans as JSON.
*/
SpiJsonWriter createJsonWriter(JsonGenerator gen, JsonWriteOptions options);
/**
* Create a Json Writer for writing beans as JSON supplying a writer.
*/
SpiJsonWriter createJsonWriter(Writer writer);
}
@@ -1,12 +1,11 @@
package io.ebeaninternal.server.changelog;
import io.ebean.ValuePair;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonGenerator;
import io.ebean.event.changelog.BeanChange;
import io.ebean.event.changelog.ChangeSet;
import io.ebean.event.changelog.ChangeType;
import io.ebean.text.json.JsonContext;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonGenerator;
import java.io.IOException;
import java.io.Writer;
@@ -15,25 +14,23 @@ import java.util.Map;
/**
* Builds JSON document for a bean change.
*/
public class ChangeJsonBuilder {
class ChangeJsonBuilder {
protected final JsonFactory jsonFactory = new JsonFactory();
protected final JsonContext json;
protected ChangeJsonBuilder(JsonContext json) {
ChangeJsonBuilder(JsonContext json) {
this.json = json;
}
/**
* Write the bean change as JSON.
*/
public void writeBeanJson(Writer writer, BeanChange bean, ChangeSet changeSet, int position) throws IOException {
void writeBeanJson(Writer writer, BeanChange bean, ChangeSet changeSet) throws IOException {
try (JsonGenerator generator = jsonFactory.createGenerator(writer)) {
writeBeanChange(generator, bean, changeSet, position);
writeBeanChange(generator, bean, changeSet);
generator.flush();
}
}
@@ -41,34 +38,29 @@ public class ChangeJsonBuilder {
/**
* Write the bean change as JSON document containing the transaction header details.
*/
protected void writeBeanChange(JsonGenerator gen, BeanChange bean, ChangeSet changeSet, int position) throws IOException {
private void writeBeanChange(JsonGenerator gen, BeanChange bean, ChangeSet changeSet) throws IOException {
gen.writeStartObject();
writeBeanTransactionDetails(gen, changeSet, position);
gen.writeStringField("object", bean.getTable());
gen.writeNumberField("ts", bean.getEventTime());
gen.writeStringField("change", bean.getEvent().getCode());
gen.writeStringField("type", bean.getType());
gen.writeStringField("id", bean.getId().toString());
if (bean.getTenantId() != null) {
gen.writeStringField("tenantId", bean.getTenantId().toString());
}
gen.writeStringField("objectId", bean.getId().toString());
gen.writeStringField("change", bean.getType().getCode());
gen.writeNumberField("eventTime", bean.getEventTime());
writeBeanTransactionDetails(gen, changeSet);
writeBeanValues(gen, bean);
gen.writeEndObject();
}
/**
* Denormalise by writing the transaction header details.
*/
protected void writeBeanTransactionDetails(JsonGenerator gen, ChangeSet changeSet, int position) throws IOException {
private void writeBeanTransactionDetails(JsonGenerator gen, ChangeSet changeSet) throws IOException {
gen.writeStringField("txnId", changeSet.getTxnId());
gen.writeStringField("txnState", changeSet.getTxnState().getCode());
gen.writeNumberField("txnBatch", changeSet.getTxnBatch());
gen.writeNumberField("txnPosition", position);
String source = changeSet.getSource();
if (source != null) {
gen.writeStringField("source", source);
@@ -94,39 +86,18 @@ public class ChangeJsonBuilder {
/**
* For insert and update write the new/old values.
*/
protected void writeBeanValues(JsonGenerator gen, BeanChange bean) throws IOException {
private void writeBeanValues(JsonGenerator gen, BeanChange bean) throws IOException {
if (bean.getType() != ChangeType.DELETE) {
gen.writeFieldName("values");
gen.writeStartObject();
writeValuePairs(bean, gen);
gen.writeEndObject();
}
}
if (bean.getEvent() != ChangeType.DELETE) {
gen.writeFieldName("data");
gen.writeRaw(":");
gen.writeRaw(bean.getData());
/**
* Write all the value pairs suppressing null values.
* <p>
* We are intentionally keeping the same new/old structure for both inserts and updates.
* </p>
*/
protected void writeValuePairs(BeanChange bean, JsonGenerator gen) throws IOException {
for (Map.Entry<String, ValuePair> entry : bean.getValues().entrySet()) {
gen.writeFieldName(entry.getKey());
gen.writeStartObject();
ValuePair value = entry.getValue();
Object newValue = value.getNewValue();
if (newValue != null) {
gen.writeFieldName("new");
json.writeScalar(gen, newValue);
String oldData = bean.getOldData();
if (oldData != null) {
gen.writeRaw(",\"oldData\":");
gen.writeRaw(oldData);
}
Object oldValue = value.getOldValue();
if (oldValue != null) {
gen.writeFieldName("old");
json.writeScalar(gen, oldValue);
}
gen.writeEndObject();
}
}
@@ -10,15 +10,10 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.StringWriter;
import java.util.List;
import java.util.Properties;
/**
* Logs the change sets in JSON to logger named <code>io.ebean.ChangeLog</code>.
* <p>
* The logged entries duplicate/denormalise the transaction details so that each bean change
* is fully contained with the transaction information.
* </p>
* Simply logs the change sets in JSON form to logger named <code>io.ebean.ChangeLog</code>.
*/
public class DefaultChangeLogListener implements ChangeLogListener, Plugin {
@@ -30,22 +25,17 @@ public class DefaultChangeLogListener implements ChangeLogListener, Plugin {
/**
* The named logger we send the change set payload to. Can be externally configured as desired.
*/
protected static final Logger changeLog = LoggerFactory.getLogger("io.ebean.ChangeLog");
private static final Logger changeLog = LoggerFactory.getLogger("io.ebean.ChangeLog");
/**
* Used to build the JSON.
*/
protected ChangeJsonBuilder jsonBuilder;
private ChangeJsonBuilder jsonBuilder;
/**
* A bigger default buffer for bean inserts and updates (that have value pairs).
*/
protected int defaultBufferSize = 400;
/**
* Expected to be a reasonable buffer size for deletes (which do not have value pairs).
*/
protected int defaultDeleteBufferSize = 250;
private int defaultBufferSize = 400;
public DefaultChangeLogListener() {
}
@@ -79,13 +69,11 @@ public class DefaultChangeLogListener implements ChangeLogListener, Plugin {
@Override
public void log(ChangeSet changeSet) {
List<BeanChange> changes = changeSet.getChanges();
for (int i = 0; i < changes.size(); i++) {
for (BeanChange beanChange : changeSet.getChanges()) {
// log each bean change as a separate log entry
BeanChange beanChange = changes.get(i);
try {
StringWriter writer = new StringWriter(getBufferSize(beanChange));
jsonBuilder.writeBeanJson(writer, beanChange, changeSet, i);
jsonBuilder.writeBeanJson(writer, beanChange, changeSet);
changeLog.info(writer.toString());
} catch (Exception e) {
logger.error("Exception logging beanChange " + beanChange.toString(), e);
@@ -96,9 +84,9 @@ public class DefaultChangeLogListener implements ChangeLogListener, Plugin {
/**
* Return a decent buffer size based on the bean change.
*/
protected int getBufferSize(BeanChange beanChange) {
private int getBufferSize(BeanChange beanChange) {
return ChangeType.DELETE == beanChange.getType() ? defaultDeleteBufferSize : defaultBufferSize;
return ChangeType.DELETE == beanChange.getEvent() ? 250 : defaultBufferSize;
}
}
@@ -51,16 +51,8 @@ import io.ebean.plugin.Plugin;
import io.ebean.plugin.SpiServer;
import io.ebean.text.csv.CsvReader;
import io.ebean.text.json.JsonContext;
import io.ebeaninternal.api.LoadBeanRequest;
import io.ebeaninternal.api.LoadManyRequest;
import io.ebeaninternal.api.ScopeTrans;
import io.ebeaninternal.api.ScopedTransaction;
import io.ebeaninternal.api.SpiBackgroundExecutor;
import io.ebeaninternal.api.SpiEbeanServer;
import io.ebeaninternal.api.SpiQuery;
import io.ebeaninternal.api.*;
import io.ebeaninternal.api.SpiQuery.Type;
import io.ebeaninternal.api.SpiTransaction;
import io.ebeaninternal.api.TransactionEventTable;
import io.ebeaninternal.server.autotune.AutoTuneService;
import io.ebeaninternal.server.core.timezone.DataTimeZone;
import io.ebeaninternal.server.deploy.BeanDescriptor;
@@ -179,7 +171,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
private final EncryptKeyManager encryptKeyManager;
private final JsonContext jsonContext;
private final SpiJsonContext jsonContext;
private final DocumentStore documentStore;
@@ -2209,6 +2201,11 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
return jsonContext;
}
@Override
public SpiJsonContext jsonExtended() {
return jsonContext;
}
@Override
public void collectQueryStats(ObjectGraphNode node, long loadedBeanCount, long timeMicros) {
@@ -3,12 +3,7 @@ package io.ebeaninternal.server.core;
import io.ebean.ValuePair;
import io.ebean.bean.EntityBean;
import io.ebeaninternal.server.deploy.BeanDescriptor;
import io.ebeaninternal.server.deploy.BeanProperty;
import io.ebeaninternal.server.deploy.BeanPropertyAssocMany;
import io.ebeaninternal.server.deploy.BeanPropertyAssocOne;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
/**
@@ -54,51 +49,4 @@ public class DiffHelp {
return desc.diff((EntityBean) newBean, (EntityBean) oldBean);
}
/**
* Flattens an existing diff map converting assoc one beans into the associated id changes.
*/
public static Map<String, ValuePair> flatten(Map<String, ValuePair> values, BeanDescriptor<?> desc) {
Map<String, ValuePair> flattened = null;
Iterator<Map.Entry<String, ValuePair>> iterator = values.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<String, ValuePair> entry = iterator.next();
BeanProperty beanProperty = desc.getBeanProperty(entry.getKey());
if (beanProperty instanceof BeanPropertyAssocMany) {
// filter out assoc many bean properties
iterator.remove();
} else if (beanProperty instanceof BeanPropertyAssocOne) {
BeanPropertyAssocOne<?> assoc = (BeanPropertyAssocOne<?>) beanProperty;
if (!assoc.isEmbedded()) {
// flatten for assoc one beans
if (flattened == null) {
flattened = new LinkedHashMap<>();
}
flattenToId(flattened, entry, beanProperty, assoc);
iterator.remove();
}
}
}
if (flattened != null) {
values.putAll(flattened);
}
return values;
}
private static void flattenToId(Map<String, ValuePair> flattened, Map.Entry<String, ValuePair> entry, BeanProperty beanProperty, BeanPropertyAssocOne<?> assoc) {
BeanDescriptor<?> oneDesc = assoc.getTargetDescriptor();
ValuePair value = entry.getValue();
Object newId = value.getNewValue() == null ? null : oneDesc.getId((EntityBean) value.getNewValue());
Object oldId = value.getOldValue() == null ? null : oneDesc.getId((EntityBean) value.getOldValue());
String propName = beanProperty.getName() + "." + oneDesc.getIdProperty().getName();
flattened.put(propName, new ValuePair(newId, oldId));
}
}
@@ -1,9 +1,10 @@
package io.ebeaninternal.server.core;
import com.fasterxml.jackson.core.JsonFactory;
import io.ebean.ExpressionFactory;
import io.ebean.annotation.Platform;
import io.ebean.cache.ServerCacheManager;
import io.ebean.config.ExternalTransactionManager;
import io.ebean.annotation.Platform;
import io.ebean.config.ServerConfig;
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebean.config.dbplatform.DbHistorySupport;
@@ -14,9 +15,9 @@ import io.ebean.event.readaudit.ReadAuditLogger;
import io.ebean.event.readaudit.ReadAuditPrepare;
import io.ebean.plugin.Plugin;
import io.ebean.plugin.SpiServer;
import io.ebean.text.json.JsonContext;
import io.ebeaninternal.api.SpiBackgroundExecutor;
import io.ebeaninternal.api.SpiEbeanServer;
import io.ebeaninternal.api.SpiJsonContext;
import io.ebeaninternal.server.autotune.AutoTuneService;
import io.ebeaninternal.server.autotune.service.AutoTuneServiceFactory;
import io.ebeaninternal.server.cache.DefaultCacheAdapter;
@@ -60,7 +61,6 @@ import io.ebeanservice.docstore.api.DocStoreFactory;
import io.ebeanservice.docstore.api.DocStoreIntegration;
import io.ebeanservice.docstore.api.DocStoreUpdateProcessor;
import io.ebeanservice.docstore.none.NoneDocStoreFactory;
import com.fasterxml.jackson.core.JsonFactory;
import org.avaje.datasource.DataSourcePool;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -261,7 +261,7 @@ public class InternalConfiguration {
}
}
public JsonContext createJsonContext(SpiEbeanServer server) {
public SpiJsonContext createJsonContext(SpiEbeanServer server) {
return new DJsonContext(server, jsonFactory, typeManager);
}
@@ -385,7 +385,7 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
*/
@Override
@SuppressWarnings("unchecked")
public Set<?> findSet() {
public Set<T> findSet() {
return (Set<T>) queryEngine.findMany(this);
}
@@ -393,7 +393,8 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
* Execute the query as findMap.
*/
@Override
public Map<?, ?> findMap() {
@SuppressWarnings("unchecked")
public <K> Map<K, T> findMap() {
String mapKey = query.getMapKey();
if (mapKey == null) {
BeanProperty idProp = beanDescriptor.getIdProperty();
@@ -403,7 +404,7 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
throw new PersistenceException("No mapKey specified for query");
}
}
return (Map<?, ?>) queryEngine.findMany(this);
return (Map<K, T>) queryEngine.findMany(this);
}
/**
@@ -480,7 +481,7 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
} else {
cacheKey = query.queryHash();
}
if (!query.getUseQueryCache().isGet()) {
return null;
}
@@ -828,17 +828,20 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
@Override
public void postExecute() {
changeLog();
if (controller != null) {
controllerPost();
}
setNotifyCache();
if (type == Type.UPDATE && (notifyCache || docStoreMode == DocStoreMode.UPDATE)) {
boolean isChangeLog = beanDescriptor.isChangeLog();
if (type == Type.UPDATE && (isChangeLog || notifyCache || docStoreMode == DocStoreMode.UPDATE)) {
// get the dirty properties for update notification to the doc store
dirtyProperties = intercept.getDirtyProperties();
}
if (isChangeLog) {
changeLog();
}
// if bean persisted again then should result in an update
intercept.setLoaded();
if (isInsert()) {
@@ -1174,4 +1177,10 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
return now;
}
/**
* Return true if this is a stateless update request (in which case it doesn't really have 'old values').
*/
public boolean isStatelessUpdate() {
return statelessUpdate;
}
}
@@ -101,12 +101,12 @@ public interface SpiOrmQueryRequest<T> extends DocQueryRequest<T> {
/**
* Execute the query as findSet.
*/
Set<?> findSet();
Set<T> findSet();
/**
* Execute the query as findMap.
*/
Map<?, ?> findMap();
<K> Map<K, T> findMap();
/**
* Execute the findSingleAttributeList query.
@@ -0,0 +1,108 @@
package io.ebeaninternal.server.deploy;
import io.ebean.PersistenceIOException;
import io.ebean.bean.BeanDiffVisitor;
import io.ebeaninternal.server.text.json.SpiJsonWriter;
import io.ebeaninternal.server.util.ArrayStack;
import java.io.IOException;
import java.io.StringWriter;
/**
* Builds the 'new values' and 'old values' in JSON form for ChangeLog.
*/
class BeanChangeJson implements BeanDiffVisitor {
private final StringWriter newData;
private final StringWriter oldData;
private final SpiJsonWriter newJson;
private final SpiJsonWriter oldJson;
private final ArrayStack<BeanDescriptor<?>> stack = new ArrayStack<>();
private BeanDescriptor<?> descriptor;
BeanChangeJson(BeanDescriptor<?> descriptor, boolean statelessUpdate) {
this.descriptor = descriptor;
this.newData = new StringWriter(200);
this.newJson = descriptor.createJsonWriter(newData);
newJson.writeStartObject();
if (statelessUpdate) {
this.oldJson = null;
this.oldData = null;
} else {
this.oldData = new StringWriter(200);
this.oldJson = descriptor.createJsonWriter(oldData);
oldJson.writeStartObject();
}
}
@Override
public void visit(int position, Object newVal, Object oldVal) {
try {
BeanProperty prop = descriptor.propertiesIndex[position];
if (prop.isDbUpdatable()) {
prop.jsonWriteValue(newJson, newVal);
if (oldJson != null) {
prop.jsonWriteValue(oldJson, oldVal);
}
}
} catch (IOException e) {
throw new PersistenceIOException(e);
}
}
@Override
public void visitPush(int position) {
stack.push(descriptor);
BeanPropertyAssocOne<?> embedded = (BeanPropertyAssocOne<?>)descriptor.propertiesIndex[position];
descriptor = embedded.getTargetDescriptor();
newJson.writeStartObject(embedded.getName());
if (oldJson != null) {
oldJson.writeStartObject(embedded.getName());
}
}
@Override
public void visitPop() {
newJson.writeEndObject();
if (oldJson != null) {
oldJson.writeEndObject();
}
descriptor = stack.pop();
}
/**
* Flush the buffers.
*/
void flush() {
try {
newJson.writeEndObject();
newJson.gen().flush();
if (oldJson != null) {
oldJson.writeEndObject();
oldJson.gen().flush();
}
} catch (IOException e) {
throw new PersistenceIOException(e);
}
}
/**
* Return the new values JSON.
*/
String newJson() {
return newData.toString();
}
/**
* Return the old values JSON.
*/
String oldJson() {
return oldData == null ? null : oldData.toString();
}
}
@@ -7,7 +7,7 @@ import io.ebean.bean.BeanCollection;
import io.ebean.bean.BeanCollectionAdd;
import io.ebean.bean.BeanCollectionLoader;
import io.ebean.bean.EntityBean;
import io.ebeaninternal.server.text.json.WriteJson;
import io.ebeaninternal.server.text.json.SpiJsonWriter;
import java.io.IOException;
@@ -62,6 +62,6 @@ public interface BeanCollectionHelp<T> {
/**
* Write the collection out as json.
*/
void jsonWrite(WriteJson ctx, String name, Object collection, boolean explicitInclude) throws IOException;
void jsonWrite(SpiJsonWriter ctx, String name, Object collection, boolean explicitInclude) throws IOException;
}
@@ -47,7 +47,6 @@ import io.ebeaninternal.server.cache.CachedBeanData;
import io.ebeaninternal.server.cache.CachedManyIds;
import io.ebeaninternal.server.core.CacheOptions;
import io.ebeaninternal.server.core.DefaultSqlUpdate;
import io.ebeaninternal.server.core.DiffHelp;
import io.ebeaninternal.server.core.InternString;
import io.ebeaninternal.server.core.PersistRequest;
import io.ebeaninternal.server.core.PersistRequestBean;
@@ -67,7 +66,7 @@ import io.ebeaninternal.server.query.CQueryPlanStats.Snapshot;
import io.ebeaninternal.server.query.SplitName;
import io.ebeaninternal.server.querydefn.OrmQueryDetail;
import io.ebeaninternal.server.text.json.ReadJson;
import io.ebeaninternal.server.text.json.WriteJson;
import io.ebeaninternal.server.text.json.SpiJsonWriter;
import io.ebeaninternal.server.type.DataBind;
import io.ebeaninternal.util.SortByClause;
import io.ebeaninternal.util.SortByClauseParser;
@@ -89,7 +88,6 @@ import java.sql.SQLException;
import java.sql.Types;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
@@ -810,11 +808,6 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
* Return true if this request should be included in the change log.
*/
public BeanChange getChangeLogBean(PersistRequestBean<T> request) {
if (changeLogFilter == null) {
return null;
}
PersistRequest.Type type = request.getType();
switch (request.getType()) {
case INSERT:
return changeLogFilter.includeInsert(request) ? insertBeanChange(request) : null;
@@ -828,30 +821,75 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
}
}
private BeanChange beanChange(ChangeType type, Object id, String data, String oldData) {
Object tenantId = ebeanServer.currentTenantId();
return new BeanChange(name, tenantId, id, type, data, oldData);
}
/**
* Return the bean change for a delete.
*/
private BeanChange deleteBeanChange(PersistRequestBean<T> request) {
return newBeanChange(request.getBeanId(), ChangeType.DELETE, Collections.<String, ValuePair>emptyMap());
return beanChange(ChangeType.DELETE, request.getBeanId(), null, null);
}
/**
* Return the bean change for an update.
* Return the bean change for an update generating 'new values' and 'old values' in JSON form.
*/
private BeanChange updateBeanChange(PersistRequestBean<T> request) {
return newBeanChange(request.getBeanId(), ChangeType.UPDATE, diffFlatten(request.getEntityBeanIntercept().getDirtyValues()));
try {
BeanChangeJson changeJson = new BeanChangeJson(this, request.isStatelessUpdate());
request.getEntityBeanIntercept().addDirtyPropertyValues(changeJson);
changeJson.flush();
return beanChange(ChangeType.UPDATE, request.getBeanId(), changeJson.newJson(), changeJson.oldJson());
} catch (RuntimeException e) {
logger.error("Failed to write ChangeLog entry for update", e);
return null;
}
}
/**
* Return the bean change for an insert.
*/
private BeanChange insertBeanChange(PersistRequestBean<T> request) {
return newBeanChange(request.getBeanId(), ChangeType.INSERT, diffForInsert(request.getEntityBean()));
try {
StringWriter writer = new StringWriter(200);
SpiJsonWriter jsonWriter = createJsonWriter(writer);
jsonWriteForInsert(jsonWriter, request.getEntityBean());
jsonWriter.gen().flush();
return beanChange(ChangeType.INSERT, request.getBeanId(), writer.toString(), null);
} catch (IOException e) {
logger.error("Failed to write ChangeLog entry for insert", e);
return null;
}
}
private BeanChange newBeanChange(Object id, ChangeType changeType, Map<String, ValuePair> values) {
Object tenantId = ebeanServer.currentTenantId();
return new BeanChange(getBaseTable(), tenantId, id, changeType, values);
SpiJsonWriter createJsonWriter(StringWriter writer) {
return ebeanServer.jsonExtended().createJsonWriter(writer);
}
/**
* Populate the diff for inserts with flattened non-null property values.
*/
protected void jsonWriteForInsert(SpiJsonWriter jsonWriter, EntityBean newBean) throws IOException {
jsonWriter.writeStartObject();
for (BeanProperty prop : propertiesBaseScalar) {
prop.jsonWriteForInsert(jsonWriter, newBean);
}
for (BeanPropertyAssocOne<?> prop : propertiesOne) {
prop.jsonWriteForInsert(jsonWriter, newBean);
}
for (BeanPropertyAssocOne<?> prop : propertiesEmbedded) {
prop.jsonWriteForInsert(jsonWriter, newBean);
}
jsonWriter.writeEndObject();
}
public SqlUpdate deleteById(Object id, List<Object> idList, boolean softDelete) {
@@ -2856,45 +2894,6 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
}
}
/**
* Flatten the diff that comes from the entity bean intercept.
*/
Map<String, ValuePair> diffFlatten(Map<String, ValuePair> diff) {
return DiffHelp.flatten(diff, this);
}
/**
* Return a map of the differences between a and b.
* <p>
* A and B must be of the same type. B can be null, in which case the 'dirty
* diff' of a is returned.
* </p>
* <p>
* This intentionally does not include as OneToMany or ManyToMany properties.
* </p>
*/
public Map<String, ValuePair> diffForInsert(EntityBean newBean) {
Map<String, ValuePair> map = new LinkedHashMap<>();
diffForInsert(null, map, newBean);
return map;
}
/**
* Populate the diff for inserts with flattened non-null property values.
*/
public void diffForInsert(String prefix, Map<String, ValuePair> map, EntityBean newBean) {
for (BeanProperty aPropertiesBaseScalar : propertiesBaseScalar) {
aPropertiesBaseScalar.diffForInsert(prefix, map, newBean);
}
for (BeanPropertyAssocOne<?> aPropertiesOne : propertiesOne) {
aPropertiesOne.diffForInsert(prefix, map, newBean);
}
for (BeanPropertyAssocOne<?> aPropertiesEmbedded : propertiesEmbedded) {
aPropertiesEmbedded.diffForInsert(prefix, map, newBean);
}
}
/**
* Return the diff comparing the bean values.
*/
@@ -3071,23 +3070,23 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
return propertiesGenUpdate;
}
public void jsonWriteDirty(WriteJson writeJson, EntityBean bean, boolean[] dirtyProps) throws IOException {
public void jsonWriteDirty(SpiJsonWriter writeJson, EntityBean bean, boolean[] dirtyProps) throws IOException {
jsonHelp.jsonWriteDirty(writeJson, bean, dirtyProps);
}
protected void jsonWriteDirtyProperties(WriteJson writeJson, EntityBean bean, boolean[] dirtyProps) throws IOException {
protected void jsonWriteDirtyProperties(SpiJsonWriter writeJson, EntityBean bean, boolean[] dirtyProps) throws IOException {
jsonHelp.jsonWriteDirtyProperties(writeJson, bean, dirtyProps);
}
public void jsonWrite(WriteJson writeJson, EntityBean bean) throws IOException {
public void jsonWrite(SpiJsonWriter writeJson, EntityBean bean) throws IOException {
jsonHelp.jsonWrite(writeJson, bean, null);
}
public void jsonWrite(WriteJson writeJson, EntityBean bean, String key) throws IOException {
public void jsonWrite(SpiJsonWriter writeJson, EntityBean bean, String key) throws IOException {
jsonHelp.jsonWrite(writeJson, bean, key);
}
protected void jsonWriteProperties(WriteJson writeJson, EntityBean bean) throws IOException {
protected void jsonWriteProperties(SpiJsonWriter writeJson, EntityBean bean) throws IOException {
jsonHelp.jsonWriteProperties(writeJson, bean);
}
@@ -6,8 +6,7 @@ import com.fasterxml.jackson.core.JsonToken;
import io.ebean.bean.EntityBean;
import io.ebean.text.json.EJson;
import io.ebeaninternal.server.text.json.ReadJson;
import io.ebeaninternal.server.text.json.WriteJson;
import io.ebeaninternal.server.text.json.WriteJson.WriteBean;
import io.ebeaninternal.server.text.json.SpiJsonWriter;
import java.io.IOException;
import java.util.LinkedHashMap;
@@ -24,7 +23,7 @@ public class BeanDescriptorJsonHelp<T> {
this.inheritInfo = desc.inheritInfo;
}
public void jsonWrite(WriteJson writeJson, EntityBean bean, String key) throws IOException {
public void jsonWrite(SpiJsonWriter writeJson, EntityBean bean, String key) throws IOException {
writeJson.writeStartObject(key);
@@ -43,13 +42,12 @@ public class BeanDescriptorJsonHelp<T> {
writeJson.writeEndObject();
}
protected void jsonWriteProperties(WriteJson writeJson, EntityBean bean) throws IOException {
protected void jsonWriteProperties(SpiJsonWriter writeJson, EntityBean bean) throws IOException {
WriteBean writeBean = writeJson.createWriteBean(desc, bean);
writeBean.write(writeJson);
writeJson.writeBean(desc, bean);
}
public void jsonWriteDirty(WriteJson writeJson, EntityBean bean, boolean[] dirtyProps) throws IOException {
public void jsonWriteDirty(SpiJsonWriter writeJson, EntityBean bean, boolean[] dirtyProps) throws IOException {
if (inheritInfo == null) {
jsonWriteDirtyProperties(writeJson, bean, dirtyProps);
@@ -58,7 +56,7 @@ public class BeanDescriptorJsonHelp<T> {
}
}
protected void jsonWriteDirtyProperties(WriteJson writeJson, EntityBean bean, boolean[] dirtyProps) throws IOException {
protected void jsonWriteDirtyProperties(SpiJsonWriter writeJson, EntityBean bean, boolean[] dirtyProps) throws IOException {
writeJson.writeStartObject(null);
// render the dirty properties
@@ -8,7 +8,7 @@ import io.ebean.bean.BeanCollectionAdd;
import io.ebean.bean.BeanCollectionLoader;
import io.ebean.bean.EntityBean;
import io.ebean.common.BeanList;
import io.ebeaninternal.server.text.json.WriteJson;
import io.ebeaninternal.server.text.json.SpiJsonWriter;
import java.io.IOException;
import java.util.ArrayList;
@@ -125,7 +125,7 @@ public final class BeanListHelp<T> implements BeanCollectionHelp<T> {
}
@Override
public void jsonWrite(WriteJson ctx, String name, Object collection, boolean explicitInclude) throws IOException {
public void jsonWrite(SpiJsonWriter ctx, String name, Object collection, boolean explicitInclude) throws IOException {
List<?> list;
if (collection instanceof BeanCollection<?>) {
@@ -8,7 +8,7 @@ import io.ebean.bean.BeanCollectionAdd;
import io.ebean.bean.BeanCollectionLoader;
import io.ebean.bean.EntityBean;
import io.ebean.common.BeanMap;
import io.ebeaninternal.server.text.json.WriteJson;
import io.ebeaninternal.server.text.json.SpiJsonWriter;
import java.io.IOException;
import java.util.LinkedHashMap;
@@ -160,7 +160,7 @@ public final class BeanMapHelp<T> implements BeanCollectionHelp<T> {
}
@Override
public void jsonWrite(WriteJson ctx, String name, Object collection, boolean explicitInclude) throws IOException {
public void jsonWrite(SpiJsonWriter ctx, String name, Object collection, boolean explicitInclude) throws IOException {
Map<?, ?> map;
if (collection instanceof BeanCollection<?>) {
@@ -24,7 +24,7 @@ import io.ebeaninternal.server.query.SplitName;
import io.ebeaninternal.server.query.SqlBeanLoad;
import io.ebeaninternal.server.query.SqlJoinType;
import io.ebeaninternal.server.text.json.ReadJson;
import io.ebeaninternal.server.text.json.WriteJson;
import io.ebeaninternal.server.text.json.SpiJsonWriter;
import io.ebeaninternal.server.type.DataBind;
import io.ebeaninternal.server.type.ScalarType;
import io.ebeaninternal.server.type.ScalarTypeBoolean;
@@ -1360,21 +1360,55 @@ public class BeanProperty implements ElPropertyValue, Property {
return jsonSerialize;
}
@SuppressWarnings(value = "unchecked")
public void jsonWrite(WriteJson writeJson, EntityBean bean) throws IOException {
/**
* JSON write the property for 'insert only depth'.
*/
@SuppressWarnings("unchecked")
public void jsonWriteForInsert(SpiJsonWriter writeJson, EntityBean bean) throws IOException {
if (!jsonSerialize) {
return;
}
Object value = getValueIntercept(bean);
Object value = getValue(bean);
if (value != null) {
jsonWriteScalar(writeJson, value);
}
}
/**
* JSON write the property value.
*/
public void jsonWriteValue(SpiJsonWriter writeJson, Object value) throws IOException {
if (!jsonSerialize) {
return;
}
jsonWriteVal(writeJson, value);
}
/**
* JSON write the bean property.
*/
public void jsonWrite(SpiJsonWriter writeJson, EntityBean bean) throws IOException {
if (!jsonSerialize) {
return;
}
jsonWriteVal(writeJson, getValueIntercept(bean));
}
@SuppressWarnings("unchecked")
private void jsonWriteVal(SpiJsonWriter writeJson, Object value) throws IOException {
if (value == null) {
writeJson.writeNullField(name);
} else {
if (scalarType != null) {
writeJson.writeFieldName(name);
scalarType.jsonWrite(writeJson.gen(), value);
} else {
writeJson.writeValueUsingObjectMapper(name, value);
}
jsonWriteScalar(writeJson, value);
}
}
private void jsonWriteScalar(SpiJsonWriter writeJson, Object value) throws IOException {
if (scalarType != null) {
writeJson.writeFieldName(name);
scalarType.jsonWrite(writeJson.gen(), value);
} else {
writeJson.writeValueUsingObjectMapper(name, value);
}
}
@@ -1407,17 +1441,6 @@ public class BeanProperty implements ElPropertyValue, Property {
}
}
/**
* Populate diff map for insert if the property is not null.
*/
public void diffForInsert(String prefix, Map<String, ValuePair> map, EntityBean newBean) {
Object newVal = (newBean == null) ? null : getValue(newBean);
if (newVal != null) {
String propName = (prefix == null) ? name : prefix + "." + name;
map.put(propName, new ValuePair(newVal, null));
}
}
/**
* Populate diff map comparing the property values between the beans.
*/
@@ -20,7 +20,7 @@ import io.ebeaninternal.server.el.ElPropertyChainBuilder;
import io.ebeaninternal.server.el.ElPropertyValue;
import io.ebeaninternal.server.query.SqlBeanLoad;
import io.ebeaninternal.server.text.json.ReadJson;
import io.ebeaninternal.server.text.json.WriteJson;
import io.ebeaninternal.server.text.json.SpiJsonWriter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -962,8 +962,15 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
return null != targetDescriptor.getId(otherBean);
}
/**
* Skip JSON write value for ToMany property.
*/
public void jsonWriteValue(SpiJsonWriter writeJson, Object value) throws IOException {
// do nothing, exclude ToMany properties
}
@Override
public void jsonWrite(WriteJson ctx, EntityBean bean) throws IOException {
public void jsonWrite(SpiJsonWriter ctx, EntityBean bean) throws IOException {
if (!this.jsonSerialize) {
return;
}
@@ -49,7 +49,7 @@ public class BeanPropertyAssocManyJsonHelp {
return;
}
if (JsonToken.START_ARRAY != event) {
throw new JsonParseException("Unexpected token " + event + " - expecting start_array ", parser.getCurrentLocation());
throw new JsonParseException(parser, "Unexpected token " + event + " - expecting start_array ");
}
if (many.isTransient()) {
@@ -18,7 +18,7 @@ import io.ebeaninternal.server.query.SplitName;
import io.ebeaninternal.server.query.SqlBeanLoad;
import io.ebeaninternal.server.query.SqlJoinType;
import io.ebeaninternal.server.text.json.ReadJson;
import io.ebeaninternal.server.text.json.WriteJson;
import io.ebeaninternal.server.text.json.SpiJsonWriter;
import javax.persistence.PersistenceException;
import java.io.IOException;
@@ -351,22 +351,6 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
return importedPrimaryKey;
}
@Override
public void diffForInsert(String prefix, Map<String, ValuePair> map, EntityBean newBean) {
Object newEmb = (newBean == null) ? null : getValue(newBean);
if (newEmb != null) {
prefix = (prefix == null) ? name : prefix + "." + name;
if (embedded) {
getTargetDescriptor().diffForInsert(prefix, map, (EntityBean) newEmb);
} else {
// we are only interested in the Id value
BeanDescriptor<T> targetDescriptor = getTargetDescriptor();
BeanProperty idProperty = targetDescriptor.getIdProperty();
idProperty.diffForInsert(prefix, map, (EntityBean) newEmb);
}
}
}
@Override
public void diff(String prefix, Map<String, ValuePair> map, EntityBean newBean, EntityBean oldBean) {
@@ -676,8 +660,57 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
}
}
/**
* JSON write property (non-recursive to other beans).
*/
@Override
public void jsonWrite(WriteJson writeJson, EntityBean bean) throws IOException {
public void jsonWriteForInsert(SpiJsonWriter writeJson, EntityBean bean) throws IOException {
if (!jsonSerialize) {
return;
}
jsonWriteBean(writeJson, getValue(bean));
}
/**
* JSON write property value (non-recursive to other beans).
*/
@Override
public void jsonWriteValue(SpiJsonWriter writeJson, Object value) throws IOException {
if (!jsonSerialize) {
return;
}
jsonWriteBean(writeJson, value);
}
private void jsonWriteBean(SpiJsonWriter writeJson, Object value) throws IOException {
if (value instanceof EntityBean) {
if (embedded) {
writeJson.writeFieldName(name);
BeanDescriptor<?> refDesc = descriptor.getBeanDescriptor(value.getClass());
refDesc.jsonWriteForInsert(writeJson, (EntityBean)value);
} else {
jsonWriteTargetId(writeJson, (EntityBean)value);
}
}
}
/**
* Just write the Id property of the ToOne property.
*/
private void jsonWriteTargetId(SpiJsonWriter writeJson, EntityBean childBean) throws IOException {
BeanProperty idProperty = targetDescriptor.getIdProperty();
if (idProperty != null) {
writeJson.writeStartObject(name);
idProperty.jsonWriteForInsert(writeJson, childBean);
writeJson.writeEndObject();
}
}
@Override
public void jsonWrite(SpiJsonWriter writeJson, EntityBean bean) throws IOException {
if (!jsonSerialize) {
return;
@@ -688,7 +721,6 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
writeJson.writeNullField(name);
} else {
//noinspection StatementWithEmptyBody
if (writeJson.isParentBean(value)) {
// bi-directional and already rendered parent
@@ -8,7 +8,7 @@ import io.ebean.bean.BeanCollectionAdd;
import io.ebean.bean.BeanCollectionLoader;
import io.ebean.bean.EntityBean;
import io.ebean.common.BeanSet;
import io.ebeaninternal.server.text.json.WriteJson;
import io.ebeaninternal.server.text.json.SpiJsonWriter;
import java.io.IOException;
import java.util.LinkedHashSet;
@@ -124,7 +124,7 @@ public final class BeanSetHelp<T> implements BeanCollectionHelp<T> {
}
@Override
public void jsonWrite(WriteJson ctx, String name, Object collection, boolean explicitInclude) throws IOException {
public void jsonWrite(SpiJsonWriter ctx, String name, Object collection, boolean explicitInclude) throws IOException {
Set<?> set;
if (collection instanceof BeanCollection<?>) {
@@ -1,26 +1,26 @@
package io.ebeaninternal.server.text.json;
import io.ebean.FetchPath;
import io.ebean.bean.EntityBean;
import io.ebean.config.JsonConfig;
import io.ebean.plugin.BeanType;
import io.ebean.text.json.EJson;
import io.ebean.text.json.JsonContext;
import io.ebean.text.json.JsonIOException;
import io.ebean.text.json.JsonReadOptions;
import io.ebean.text.json.JsonWriteBeanVisitor;
import io.ebean.text.json.JsonWriteOptions;
import io.ebeaninternal.api.SpiEbeanServer;
import io.ebeaninternal.server.deploy.BeanDescriptor;
import io.ebeaninternal.server.type.TypeManager;
import io.ebeaninternal.util.ParamTypeHelper;
import io.ebeaninternal.util.ParamTypeHelper.ManyType;
import io.ebeaninternal.util.ParamTypeHelper.TypeInfo;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import io.ebean.FetchPath;
import io.ebean.bean.EntityBean;
import io.ebean.config.JsonConfig;
import io.ebean.plugin.BeanType;
import io.ebean.text.json.EJson;
import io.ebean.text.json.JsonIOException;
import io.ebean.text.json.JsonReadOptions;
import io.ebean.text.json.JsonWriteBeanVisitor;
import io.ebean.text.json.JsonWriteOptions;
import io.ebeaninternal.api.SpiEbeanServer;
import io.ebeaninternal.api.SpiJsonContext;
import io.ebeaninternal.server.deploy.BeanDescriptor;
import io.ebeaninternal.server.type.TypeManager;
import io.ebeaninternal.util.ParamTypeHelper;
import io.ebeaninternal.util.ParamTypeHelper.ManyType;
import io.ebeaninternal.util.ParamTypeHelper.TypeInfo;
import java.io.IOException;
import java.io.Reader;
@@ -39,7 +39,7 @@ import java.util.Set;
/**
* Default implementation of JsonContext.
*/
public class DJsonContext implements JsonContext {
public class DJsonContext implements SpiJsonContext {
private final SpiEbeanServer server;
@@ -179,7 +179,7 @@ public class DJsonContext implements JsonContext {
if (currentToken != JsonToken.START_ARRAY) {
JsonToken event = src.nextToken();
if (event != JsonToken.START_ARRAY) {
throw new JsonParseException("Expecting start_array event but got " + event, src.getCurrentLocation());
throw new JsonParseException(src, "Expecting start_array event but got " + event);
}
}
@@ -336,9 +336,23 @@ public class DJsonContext implements JsonContext {
BeanDescriptor<?> d = getDescriptor(value.getClass());
WriteJson writeJson = createWriteJson(gen, options);
d.jsonWrite(writeJson, (EntityBean) value, null);
} else {
jsonScalar.write(gen, value);
}
}
@Override
public SpiJsonWriter createJsonWriter(Writer writer) {
JsonGenerator generator = createGenerator(writer);
return createJsonWriter(generator, null);
}
@Override
public SpiJsonWriter createJsonWriter(JsonGenerator gen, JsonWriteOptions options) {
return createWriteJson(gen, options);
}
private WriteJson createWriteJson(JsonGenerator gen, JsonWriteOptions options) {
FetchPath pathProps = (options == null) ? null : options.getPathProperties();
Map<String, JsonWriteBeanVisitor<?>> visitors = (options == null) ? null : options.getVisitorMap();
@@ -0,0 +1,68 @@
package io.ebeaninternal.server.text.json;
import io.ebean.bean.EntityBean;
import io.ebean.text.json.JsonWriter;
import io.ebeaninternal.server.deploy.BeanDescriptor;
import java.util.Collection;
/**
* Internal API extensions for JSON writing of Bean properties.
*/
public interface SpiJsonWriter extends JsonWriter {
/**
* Return true if the value is a parent bean.
*/
boolean isParentBean(Object value);
/**
* Start an assoc one path.
*/
void beginAssocOne(String name, EntityBean bean);
/**
* End an assoc one path.
*/
void endAssocOne();
/**
* Return true if the many property should be included.
*/
Boolean includeMany(String name);
/**
* Push the parent bean of a ToMany.
*/
void pushParentBeanMany(EntityBean bean);
/**
* Pop the parent of a ToMany.
*/
void popParentBeanMany();
/**
* Write the collection.
*/
void toJson(String name, Collection<?> collection);
/**
* Start a Many.
*/
void beginAssocMany(String name);
/**
* End a Many.
*/
void endAssocMany();
/**
* Write value using underlying Jaskson object mapper if available.
*/
void writeValueUsingObjectMapper(String name, Object value);
/**
* Write the bean properties.
*/
<T> void writeBean(BeanDescriptor<T> desc, EntityBean bean);
}
@@ -1,18 +1,17 @@
package io.ebeaninternal.server.text.json;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.ebean.FetchPath;
import io.ebean.bean.EntityBean;
import io.ebean.config.JsonConfig;
import io.ebean.text.json.EJson;
import io.ebean.text.json.JsonIOException;
import io.ebean.text.json.JsonWriteBeanVisitor;
import io.ebean.text.json.JsonWriter;
import io.ebeaninternal.api.SpiEbeanServer;
import io.ebeaninternal.server.deploy.BeanDescriptor;
import io.ebeaninternal.server.deploy.BeanProperty;
import io.ebeaninternal.server.util.ArrayStack;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
import java.io.InputStream;
@@ -21,7 +20,7 @@ import java.util.Collection;
import java.util.Map;
import java.util.Set;
public class WriteJson implements JsonWriter {
public class WriteJson implements SpiJsonWriter {
private final SpiEbeanServer server;
@@ -347,7 +346,7 @@ public class WriteJson implements JsonWriter {
return !parentBeans.isEmpty() && parentBeans.contains(bean);
}
public void pushParentBeanMany(Object parentBean) {
public void pushParentBeanMany(EntityBean parentBean) {
parentBeans.push(parentBean);
}
@@ -355,7 +354,7 @@ public class WriteJson implements JsonWriter {
parentBeans.pop();
}
public void beginAssocOne(String key, Object bean) {
public void beginAssocOne(String key, EntityBean bean) {
parentBeans.push(bean);
pathStack.pushPathKey(key);
}
@@ -384,7 +383,12 @@ public class WriteJson implements JsonWriter {
}
}
public WriteBean createWriteBean(BeanDescriptor<?> desc, EntityBean bean) {
@Override
public <T> void writeBean(BeanDescriptor<T> desc, EntityBean bean) {
createWriteBean(desc, bean).write(this);
}
private <T> WriteBean createWriteBean(BeanDescriptor<T> desc, EntityBean bean) {
String path = pathStack.peekWithNull();
JsonWriteBeanVisitor<?> visitor = (visitors == null) ? null : visitors.get(path);
@@ -436,7 +440,7 @@ public class WriteJson implements JsonWriter {
final Set<String> currentIncludeProps;
final BeanDescriptor<?> desc;
final EntityBean currentBean;
@SuppressWarnings("rawtypes")
final JsonWriteBeanVisitor visitor;