http://git-wip-us.apache.org/repos/asf/polygene-java/blob/7c2814ee/core/spi/src/main/java/org/apache/polygene/spi/entitystore/helpers/MapEntityStoreMixin.java ---------------------------------------------------------------------- diff --git a/core/spi/src/main/java/org/apache/polygene/spi/entitystore/helpers/MapEntityStoreMixin.java b/core/spi/src/main/java/org/apache/polygene/spi/entitystore/helpers/MapEntityStoreMixin.java index dc4db58..ef9816c 100644 --- a/core/spi/src/main/java/org/apache/polygene/spi/entitystore/helpers/MapEntityStoreMixin.java +++ b/core/spi/src/main/java/org/apache/polygene/spi/entitystore/helpers/MapEntityStoreMixin.java @@ -14,12 +14,9 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * - * */ package org.apache.polygene.spi.entitystore.helpers; -import java.io.IOException; import java.io.Reader; import java.io.Writer; import java.time.Instant; @@ -28,7 +25,15 @@ import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.function.Function; import java.util.stream.Stream; +import javax.json.Json; +import javax.json.JsonArray; +import javax.json.JsonArrayBuilder; +import javax.json.JsonException; +import javax.json.JsonObject; +import javax.json.JsonObjectBuilder; +import javax.json.JsonValue; import org.apache.polygene.api.common.Optional; import org.apache.polygene.api.common.QualifiedName; import org.apache.polygene.api.entity.EntityDescriptor; @@ -39,15 +44,11 @@ import org.apache.polygene.api.identity.StringIdentity; import org.apache.polygene.api.injection.scope.Service; import org.apache.polygene.api.injection.scope.Structure; import org.apache.polygene.api.injection.scope.This; -import org.apache.polygene.api.service.qualifier.Tagged; import org.apache.polygene.api.structure.Application; import org.apache.polygene.api.structure.ModuleDescriptor; -import org.apache.polygene.api.type.ValueType; import org.apache.polygene.api.unitofwork.NoSuchEntityTypeException; import org.apache.polygene.api.usecase.Usecase; -import org.apache.polygene.api.value.ValueSerialization; -import org.apache.polygene.api.value.ValueSerializationException; -import org.apache.polygene.spi.PolygeneSPI; +import org.apache.polygene.serialization.javaxjson.JavaxJson; import org.apache.polygene.spi.entity.EntityState; import org.apache.polygene.spi.entity.EntityStatus; import org.apache.polygene.spi.entitystore.DefaultEntityStoreUnitOfWork; @@ -56,11 +57,7 @@ import org.apache.polygene.spi.entitystore.EntityStoreException; import org.apache.polygene.spi.entitystore.EntityStoreSPI; import org.apache.polygene.spi.entitystore.EntityStoreUnitOfWork; import org.apache.polygene.spi.entitystore.StateCommitter; -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; -import org.json.JSONTokener; -import org.json.JSONWriter; +import org.apache.polygene.spi.serialization.JsonSerialization; /** * Implementation of EntityStore that works with an implementation of MapEntityStore. @@ -80,14 +77,10 @@ public class MapEntityStoreMixin private EntityStoreSPI entityStoreSpi; @Structure - private PolygeneSPI spi; - - @Structure private Application application; @Service - @Tagged( ValueSerialization.Formats.JSON ) - private ValueSerialization valueSerialization; + private JsonSerialization jsonSerialization; @Optional @Service @@ -97,58 +90,70 @@ public class MapEntityStoreMixin private IdentityGenerator identityGenerator; @Override - public void activateMapEntityStore() - throws Exception - { - } + public void activateMapEntityStore() {} // EntityStore @Override - public EntityStoreUnitOfWork newUnitOfWork( ModuleDescriptor module, Usecase usecaseMetaInfo, Instant currentTime ) + public EntityStoreUnitOfWork newUnitOfWork( ModuleDescriptor module, Usecase usecase, Instant currentTime ) { - return new DefaultEntityStoreUnitOfWork( module, entityStoreSpi, newUnitOfWorkId(), usecaseMetaInfo, currentTime ); + return new DefaultEntityStoreUnitOfWork( module, entityStoreSpi, newUnitOfWorkId(), + usecase, currentTime ); } // EntityStoreSPI @Override - public EntityState newEntityState( EntityStoreUnitOfWork unitOfWork, - EntityReference reference, - EntityDescriptor entityDescriptor - ) + public EntityState newEntityState( EntityStoreUnitOfWork uow, + EntityReference reference, EntityDescriptor entityDescriptor ) { - return new DefaultEntityState( unitOfWork.currentTime(), reference, entityDescriptor ); + return new DefaultEntityState( uow.currentTime(), reference, entityDescriptor ); } @Override - public synchronized EntityState entityStateOf( EntityStoreUnitOfWork unitofwork, - ModuleDescriptor module, - EntityReference reference - ) + public synchronized EntityState entityStateOf( EntityStoreUnitOfWork uow, + ModuleDescriptor module, EntityReference reference ) { - Reader in = mapEntityStore.get( reference ); - return readEntityState( module, in ); + try + { + Reader in = mapEntityStore.get( reference ); + EntityState loadedState = readEntityState( module, in ); + if( loadedState.status() == EntityStatus.UPDATED ) + { + List<EntityState> migrated = new ArrayList<>( 1 ); + migrated.add( loadedState ); + synchMigratedEntities( migrated ); + } + return loadedState; + } + catch( EntityStoreException ex ) + { + throw ex; + } + catch( Exception ex ) + { + throw new EntityStoreException( ex ); + } } @Override - public synchronized String versionOf( EntityStoreUnitOfWork unitofwork, - EntityReference reference - ) + public synchronized String versionOf( EntityStoreUnitOfWork uow, EntityReference reference ) { - Reader in = mapEntityStore.get( reference ); try { - JSONObject jsonObject = new JSONObject( new JSONTokener( in ) ); - return jsonObject.getString( JSONKeys.VERSION ); + Reader in = mapEntityStore.get( reference ); + return Json.createReader( in ).readObject().getString( JSONKeys.VERSION ); } - catch( JSONException e ) + catch( EntityStoreException ex ) { - throw new EntityStoreException( e ); + throw ex; + } + catch( Exception ex ) + { + throw new EntityStoreException( ex ); } } @Override - public StateCommitter applyChanges( final EntityStoreUnitOfWork unitofwork, final Iterable<EntityState> state - ) + public StateCommitter applyChanges( EntityStoreUnitOfWork uow, Iterable<EntityState> state ) throws EntityStoreException { return new StateCommitter() @@ -158,34 +163,47 @@ public class MapEntityStoreMixin { try { - mapEntityStore.applyChanges( changer -> { - for( EntityState entityState : state ) + mapEntityStore.applyChanges( + changer -> { - DefaultEntityState state1 = (DefaultEntityState) entityState; - if( state1.status().equals( EntityStatus.NEW ) ) + for( EntityState entityState : state ) { - try (Writer writer = changer.newEntity( state1.entityReference(), state1.entityDescriptor() )) + DefaultEntityState state1 = (DefaultEntityState) entityState; + String newVersion = uow.identity().toString(); + Instant lastModified = uow.currentTime(); + if( state1.status().equals( EntityStatus.NEW ) ) { - writeEntityState( state1, writer, unitofwork.identity().toString(), unitofwork.currentTime() ); + try( Writer writer = changer.newEntity( state1.entityReference(), + state1.entityDescriptor() ) ) + { + writeEntityState( state1, writer, newVersion, lastModified ); + } } - } - else if( state1.status().equals( EntityStatus.UPDATED ) ) - { - try (Writer writer = changer.updateEntity( state1.entityReference(), state1.entityDescriptor() )) + else if( state1.status().equals( EntityStatus.UPDATED ) ) { - writeEntityState( state1, writer, unitofwork.identity().toString(), unitofwork.currentTime() ); + MapEntityStore.MapChange mapChange = new MapEntityStore.MapChange( + state1.entityReference(), state1.entityDescriptor(), + state1.version(), newVersion, lastModified + ); + try( Writer writer = changer.updateEntity( mapChange ) ) + { + writeEntityState( state1, writer, newVersion, lastModified ); + } + } + else if( state1.status().equals( EntityStatus.REMOVED ) ) + { + changer.removeEntity( state1.entityReference(), state1.entityDescriptor() ); } } - else if( state1.status().equals( EntityStatus.REMOVED ) ) - { - changer.removeEntity( state1.entityReference(), state1.entityDescriptor() ); - } - } - } ); + } ); + } + catch( EntityStoreException ex ) + { + throw ex; } - catch( IOException e ) + catch( Exception ex ) { - throw new EntityStoreException( e ); + throw new EntityStoreException( ex ); } } @@ -197,62 +215,90 @@ public class MapEntityStoreMixin } @Override - public Stream<EntityState> entityStates( final ModuleDescriptor module ) + public Stream<EntityState> entityStates( ModuleDescriptor module ) { - List<EntityState> migrated = new ArrayList<>(); - return mapEntityStore - .entityStates() - .map( reader -> - { - EntityState entity = readEntityState( module, reader ); - if( entity.status() == EntityStatus.UPDATED ) - { - migrated.add( entity ); - // Synch back 100 at a time - if( migrated.size() > 100 ) - { - try - { - synchMigratedEntities( migrated ); - } - catch( IOException e ) - { - throw new EntityStoreException( "Synchronization of Migrated Entities failed.", e ); - } - } - } - return entity; - } ) - .onClose( () -> - { - // Synch any remaining migrated entities - if( !migrated.isEmpty() ) - { - try - { - synchMigratedEntities( migrated ); - } - catch( IOException e ) - { - throw new EntityStoreException( "Synchronization of Migrated Entities failed.", e ); - } - } - } ); + try + { + Stream<Reader> stateStream = mapEntityStore.entityStates(); + List<EntityState> migrated = new ArrayList<>(); + String migrationErrorMsg = "Synchronization of Migrated Entities failed."; + Function<Reader, EntityState> function = reader -> + { + EntityState entity = readEntityState( module, reader ); + if( entity.status() == EntityStatus.UPDATED ) + { + migrated.add( entity ); + // Sync back 100 at a time + if( migrated.size() > 100 ) + { + try + { + synchMigratedEntities( migrated ); + } + catch( EntityStoreException ex ) + { + throw ex; + } + catch( Exception ex ) + { + throw new EntityStoreException( migrationErrorMsg, ex ); + } + } + } + return entity; + }; + Runnable closer = () -> + { + // Sync any remaining migrated entities + if( !migrated.isEmpty() ) + { + try + { + synchMigratedEntities( migrated ); + } + catch( EntityStoreException ex ) + { + throw ex; + } + catch( Exception ex ) + { + throw new EntityStoreException( migrationErrorMsg, ex ); + } + } + }; + return stateStream.map( function ).onClose( closer ); + } + catch( EntityStoreException ex ) + { + throw ex; + } + catch( Exception ex ) + { + throw new EntityStoreException( ex ); + } } private void synchMigratedEntities( final List<EntityState> migratedEntities ) - throws IOException + throws Exception { - mapEntityStore.applyChanges( changer -> { - for( EntityState migratedEntity : migratedEntities ) + mapEntityStore.applyChanges( + changer -> { - DefaultEntityState state = (DefaultEntityState) migratedEntity; - try (Writer writer = changer.updateEntity( state.entityReference(), state.entityDescriptor() )) + for( EntityState migratedEntity : migratedEntities ) { - writeEntityState( state, writer, state.version(), state.lastModified() ); + DefaultEntityState state = (DefaultEntityState) migratedEntity; + String version = state.version(); + Instant lastModified = state.lastModified(); + MapEntityStore.MapChange mapChange = new MapEntityStore.MapChange( + state.entityReference(), state.entityDescriptor(), + version, version, lastModified + ); + try( Writer writer = changer.updateEntity( mapChange ) ) + { + writeEntityState( state, writer, version, lastModified ); + } } - } - } ); + } ); migratedEntities.clear(); } @@ -261,87 +307,63 @@ public class MapEntityStoreMixin return identityGenerator.generate( EntityStore.class ); } - protected void writeEntityState(DefaultEntityState state, Writer writer, String version, Instant lastModified ) + protected void writeEntityState( DefaultEntityState state, Writer writer, String version, Instant lastModified ) throws EntityStoreException { try { - JSONWriter json = new JSONWriter( writer ); - JSONWriter properties = json.object(). - key( JSONKeys.IDENTITY ).value( state.entityReference().identity() ). - key( JSONKeys.APPLICATION_VERSION ).value( application.version() ). - key( JSONKeys.TYPE ).value( state.entityDescriptor().types().findFirst().get().getName() ). - key( JSONKeys.VERSION ).value( version ). - key( JSONKeys.MODIFIED ).value( lastModified.toEpochMilli() ). - key( JSONKeys.PROPERTIES ).object(); + JsonObjectBuilder json = Json.createObjectBuilder(); + json.add( JSONKeys.IDENTITY, state.entityReference().identity().toString() ); + json.add( JSONKeys.APPLICATION_VERSION, application.version() ); + json.add( JSONKeys.TYPE, state.entityDescriptor().primaryType().getName() ); + json.add( JSONKeys.VERSION, version ); + json.add( JSONKeys.MODIFIED, lastModified.toEpochMilli() ); + JsonObjectBuilder properties = Json.createObjectBuilder(); EntityDescriptor entityType = state.entityDescriptor(); - entityType.state().properties().forEach( persistentProperty -> { - Object value = state.properties().get( persistentProperty.qualifiedName() ); - try - { - json.key( persistentProperty.qualifiedName().name() ); - if( value == null || ValueType.isPrimitiveValue( value ) ) - { - json.value( value ); - } - else - { - String serialized = valueSerialization.serialize( value ); - if( serialized.startsWith( "{" ) ) - { - json.value( new JSONObject( serialized ) ); - } - else if( serialized.startsWith( "[" ) ) - { - json.value( new JSONArray( serialized ) ); - } - else - { - json.value( serialized ); - } - } - } - catch( JSONException e ) + entityType.state().properties().forEach( + persistentProperty -> { - throw new ValueSerializationException( "Unable to write property " + persistentProperty, e ); - } - } ); - - JSONWriter associations = properties.endObject().key( JSONKeys.ASSOCIATIONS ).object(); - for( Map.Entry<QualifiedName, EntityReference> stateNameEntityReferenceEntry : state.associations() - .entrySet() ) + Object value = state.properties().get( persistentProperty.qualifiedName() ); + JsonValue jsonValue = jsonSerialization.toJson( value ); + properties.add( persistentProperty.qualifiedName().name(), jsonValue ); + } ); + json.add( JSONKeys.PROPERTIES, properties.build() ); + + JsonObjectBuilder associations = Json.createObjectBuilder(); + for( Map.Entry<QualifiedName, EntityReference> entry : state.associations().entrySet() ) { - EntityReference value = stateNameEntityReferenceEntry.getValue(); - associations.key( stateNameEntityReferenceEntry.getKey().name() ). - value( value != null ? value.identity() : null ); + EntityReference value = entry.getValue(); + associations.add( entry.getKey().name(), value == null ? null : value.identity().toString() ); } + json.add( JSONKeys.ASSOCIATIONS, associations.build() ); - JSONWriter manyAssociations = associations.endObject().key( JSONKeys.MANY_ASSOCIATIONS ).object(); - for( Map.Entry<QualifiedName, List<EntityReference>> stateNameListEntry : state.manyAssociations() - .entrySet() ) + JsonObjectBuilder manyAssociations = Json.createObjectBuilder(); + for( Map.Entry<QualifiedName, List<EntityReference>> entry : state.manyAssociations().entrySet() ) { - JSONWriter assocs = manyAssociations.key( stateNameListEntry.getKey().name() ).array(); - for( EntityReference entityReference : stateNameListEntry.getValue() ) + JsonArrayBuilder arrayBuilder = Json.createArrayBuilder(); + for( EntityReference entityReference : entry.getValue() ) { - assocs.value( entityReference.identity() ); + arrayBuilder.add( entityReference.identity().toString() ); } - assocs.endArray(); + manyAssociations.add( entry.getKey().name(), arrayBuilder.build() ); } + json.add( JSONKeys.MANY_ASSOCIATIONS, manyAssociations.build() ); - JSONWriter namedAssociations = manyAssociations.endObject().key( JSONKeys.NAMED_ASSOCIATIONS ).object(); - for( Map.Entry<QualifiedName, Map<String, EntityReference>> stateNameMapEntry : state.namedAssociations() - .entrySet() ) + JsonObjectBuilder namedAssociations = Json.createObjectBuilder(); + for( Map.Entry<QualifiedName, Map<String, EntityReference>> entry : state.namedAssociations().entrySet() ) { - JSONWriter assocs = namedAssociations.key( stateNameMapEntry.getKey().name() ).object(); - for( Map.Entry<String, EntityReference> namedRef : stateNameMapEntry.getValue().entrySet() ) + JsonObjectBuilder objectBuilder = Json.createObjectBuilder(); + for( Map.Entry<String, EntityReference> namedRef : entry.getValue().entrySet() ) { - assocs.key( namedRef.getKey() ).value( namedRef.getValue().identity() ); + objectBuilder.add( namedRef.getKey(), namedRef.getValue().identity().toString() ); } - assocs.endObject(); + namedAssociations.add( entry.getKey().name(), objectBuilder.build() ); } - namedAssociations.endObject().endObject(); + json.add( JSONKeys.NAMED_ASSOCIATIONS, namedAssociations.build() ); + JsonObject jsonState = json.build(); + Json.createWriter( writer ).write( jsonState ); } - catch( JSONException e ) + catch( Exception e ) { throw new EntityStoreException( "Could not store EntityState", e ); } @@ -352,31 +374,38 @@ public class MapEntityStoreMixin { try { - JSONObject jsonObject = new JSONObject( new JSONTokener( entityState ) ); + JsonObject parsedState = Json.createReader( entityState ).readObject(); + JsonObjectBuilder jsonStateBuilder = JavaxJson.toBuilder( parsedState ); final EntityStatus[] status = { EntityStatus.LOADED }; - String version = jsonObject.getString( JSONKeys.VERSION ); - Instant modified = Instant.ofEpochMilli(jsonObject.getLong( JSONKeys.MODIFIED )); - Identity identity = new StringIdentity(jsonObject.getString( JSONKeys.IDENTITY )); + String version = parsedState.getString( JSONKeys.VERSION ); + Instant modified = Instant.ofEpochMilli( parsedState.getJsonNumber( JSONKeys.MODIFIED ).longValueExact() ); + Identity identity = new StringIdentity( parsedState.getString( JSONKeys.IDENTITY ) ); // Check if version is correct - String currentAppVersion = jsonObject.optString( JSONKeys.APPLICATION_VERSION, "0.0" ); - if( !currentAppVersion.equals( application.version() ) ) + JsonObject state; + String currentAppVersion = parsedState.getString( JSONKeys.APPLICATION_VERSION, "0.0" ); + if( currentAppVersion.equals( application.version() ) ) + { + state = jsonStateBuilder.build(); + } + else { if( migration != null ) { - migration.migrate( jsonObject, application.version(), this ); + state = migration.migrate( jsonStateBuilder.build(), application.version(), this ); } else { // Do nothing - set version to be correct - jsonObject.put( JSONKeys.APPLICATION_VERSION, application.version() ); + jsonStateBuilder.add( JSONKeys.APPLICATION_VERSION, application.version() ); + state = jsonStateBuilder.build(); } // State changed status[ 0 ] = EntityStatus.UPDATED; } - String type = jsonObject.getString( JSONKeys.TYPE ); + String type = state.getString( JSONKeys.TYPE ); EntityDescriptor entityDescriptor = module.entityDescriptor( type ); if( entityDescriptor == null ) @@ -385,107 +414,101 @@ public class MapEntityStoreMixin } Map<QualifiedName, Object> properties = new HashMap<>(); - JSONObject props = jsonObject.getJSONObject( JSONKeys.PROPERTIES ); - entityDescriptor.state().properties().forEach( propertyDescriptor -> { - Object jsonValue; - try + JsonObject props = state.getJsonObject( JSONKeys.PROPERTIES ); + entityDescriptor.state().properties().forEach( + property -> { - jsonValue = props.get( propertyDescriptor.qualifiedName().name() ); - if( JSONObject.NULL.equals( jsonValue ) ) + try { - properties.put( propertyDescriptor.qualifiedName(), null ); + JsonValue jsonValue = props.get( property.qualifiedName().name() ); + Object value = jsonSerialization.fromJson( module, property.valueType(), jsonValue ); + properties.put( property.qualifiedName(), value ); } - else + catch( JsonException e ) { - Object value = valueSerialization.deserialize( module, propertyDescriptor.valueType(), jsonValue - .toString() ); - properties.put( propertyDescriptor.qualifiedName(), value ); + // Value not found, default it + Object initialValue = property.resolveInitialValue( module ); + properties.put( property.qualifiedName(), initialValue ); + status[ 0 ] = EntityStatus.UPDATED; } - } - catch( JSONException e ) - { - // Value not found, default it - Object initialValue = propertyDescriptor.resolveInitialValue(module); - properties.put( propertyDescriptor.qualifiedName(), initialValue ); - status[ 0 ] = EntityStatus.UPDATED; - } - } ); + } ); Map<QualifiedName, EntityReference> associations = new HashMap<>(); - JSONObject assocs = jsonObject.getJSONObject( JSONKeys.ASSOCIATIONS ); - entityDescriptor.state().associations().forEach( associationType -> { - try + JsonObject assocs = state.getJsonObject( JSONKeys.ASSOCIATIONS ); + entityDescriptor.state().associations().forEach( + association -> { - Object jsonValue = assocs.get( associationType.qualifiedName().name() ); - EntityReference value = jsonValue == JSONObject.NULL - ? null - : EntityReference.parseEntityReference( (String) jsonValue ); - associations.put( associationType.qualifiedName(), value ); - } - catch( JSONException e ) - { - // Association not found, default it to null - associations.put( associationType.qualifiedName(), null ); - status[ 0 ] = EntityStatus.UPDATED; - } - } ); + try + { + String jsonValue = assocs.getString( association.qualifiedName().name(), null ); + EntityReference value = jsonValue == null + ? null + : EntityReference.parseEntityReference( jsonValue ); + associations.put( association.qualifiedName(), value ); + } + catch( JsonException e ) + { + // Association not found, default it to null + associations.put( association.qualifiedName(), null ); + status[ 0 ] = EntityStatus.UPDATED; + } + } ); - JSONObject manyAssocs = jsonObject.getJSONObject( JSONKeys.MANY_ASSOCIATIONS ); + JsonObject manyAssocs = state.getJsonObject( JSONKeys.MANY_ASSOCIATIONS ); Map<QualifiedName, List<EntityReference>> manyAssociations = new HashMap<>(); - entityDescriptor.state().manyAssociations().forEach( manyAssociationType -> { - List<EntityReference> references = new ArrayList<>(); - try + entityDescriptor.state().manyAssociations().forEach( + association -> { - JSONArray jsonValues = manyAssocs.getJSONArray( manyAssociationType.qualifiedName().name() ); - for( int i = 0; i < jsonValues.length(); i++ ) + List<EntityReference> references = new ArrayList<>(); + try { - Object jsonValue = jsonValues.getString( i ); - EntityReference value = jsonValue == JSONObject.NULL - ? null - : EntityReference.parseEntityReference( (String) jsonValue ); - references.add( value ); + JsonArray jsonValues = manyAssocs.getJsonArray( association.qualifiedName().name() ); + for( int i = 0; i < jsonValues.size(); i++ ) + { + String jsonValue = jsonValues.getString( i, null ); + EntityReference value = jsonValue == null + ? null + : EntityReference.parseEntityReference( jsonValue ); + references.add( value ); + } + manyAssociations.put( association.qualifiedName(), references ); } - manyAssociations.put( manyAssociationType.qualifiedName(), references ); - } - catch( JSONException e ) - { - // ManyAssociation not found, default to empty one - manyAssociations.put( manyAssociationType.qualifiedName(), references ); - } - } ); + catch( JsonException e ) + { + // ManyAssociation not found, default to empty one + manyAssociations.put( association.qualifiedName(), references ); + } + } ); - JSONObject namedAssocs = jsonObject.getJSONObject( JSONKeys.NAMED_ASSOCIATIONS ); + JsonObject namedAssocs = state.getJsonObject( JSONKeys.NAMED_ASSOCIATIONS ); Map<QualifiedName, Map<String, EntityReference>> namedAssociations = new HashMap<>(); - entityDescriptor.state().namedAssociations().forEach( namedAssociationType -> { - Map<String, EntityReference> references = new LinkedHashMap<>(); - try + entityDescriptor.state().namedAssociations().forEach( + association -> { - JSONObject jsonValues = namedAssocs.getJSONObject( namedAssociationType.qualifiedName().name() ); - JSONArray names = jsonValues.names(); - if( names != null ) + Map<String, EntityReference> references = new LinkedHashMap<>(); + try { - for( int idx = 0; idx < names.length(); idx++ ) + JsonObject jsonValues = namedAssocs.getJsonObject( association.qualifiedName().name() ); + for( String name : jsonValues.keySet() ) { - String name = names.getString( idx ); - Object value = jsonValues.get( name ); - EntityReference ref = value == JSONObject.NULL + String value = jsonValues.getString( name, null ); + EntityReference ref = value == null ? null - : EntityReference.parseEntityReference( (String) value ); + : EntityReference.parseEntityReference( value ); references.put( name, ref ); } + namedAssociations.put( association.qualifiedName(), references ); } - namedAssociations.put( namedAssociationType.qualifiedName(), references ); - } - catch( JSONException e ) - { - // NamedAssociation not found, default to empty one - namedAssociations.put( namedAssociationType.qualifiedName(), references ); - } - } ); + catch( JsonException e ) + { + // NamedAssociation not found, default to empty one + namedAssociations.put( association.qualifiedName(), references ); + } + } ); return new DefaultEntityState( version, modified, - EntityReference.create(identity), + EntityReference.create( identity ), status[ 0 ], entityDescriptor, properties, @@ -494,25 +517,26 @@ public class MapEntityStoreMixin namedAssociations ); } - catch( JSONException e ) + catch( Exception e ) { throw new EntityStoreException( e ); } } @Override - public JSONObject jsonStateOf( String id ) - throws IOException + public JsonObject jsonStateOf( String id ) { - JSONObject jsonObject; - try (Reader reader = mapEntityStore.get( EntityReference.parseEntityReference( id ) )) + try( Reader reader = mapEntityStore.get( EntityReference.parseEntityReference( id ) ) ) + { + return Json.createReader( reader ).readObject(); + } + catch( EntityStoreException ex ) { - jsonObject = new JSONObject( new JSONTokener( reader ) ); + throw ex; } - catch( JSONException e ) + catch( Exception ex ) { - throw new IOException( e ); + throw new EntityStoreException( ex ); } - return jsonObject; } }
http://git-wip-us.apache.org/repos/asf/polygene-java/blob/7c2814ee/core/spi/src/main/java/org/apache/polygene/spi/entitystore/helpers/Migration.java ---------------------------------------------------------------------- diff --git a/core/spi/src/main/java/org/apache/polygene/spi/entitystore/helpers/Migration.java b/core/spi/src/main/java/org/apache/polygene/spi/entitystore/helpers/Migration.java index 4b5e8e6..6a8ebfb 100644 --- a/core/spi/src/main/java/org/apache/polygene/spi/entitystore/helpers/Migration.java +++ b/core/spi/src/main/java/org/apache/polygene/spi/entitystore/helpers/Migration.java @@ -20,14 +20,14 @@ package org.apache.polygene.spi.entitystore.helpers; -import org.json.JSONException; -import org.json.JSONObject; +import javax.json.JsonException; +import javax.json.JsonObject; /** * State Migration SPI. */ public interface Migration { - boolean migrate( JSONObject state, String toVersion, StateStore stateStore ) - throws JSONException; + JsonObject migrate( JsonObject state, String toVersion, StateStore stateStore ) + throws JsonException; } http://git-wip-us.apache.org/repos/asf/polygene-java/blob/7c2814ee/core/spi/src/main/java/org/apache/polygene/spi/entitystore/helpers/StateStore.java ---------------------------------------------------------------------- diff --git a/core/spi/src/main/java/org/apache/polygene/spi/entitystore/helpers/StateStore.java b/core/spi/src/main/java/org/apache/polygene/spi/entitystore/helpers/StateStore.java index 1f1f728..8aa9e6e 100644 --- a/core/spi/src/main/java/org/apache/polygene/spi/entitystore/helpers/StateStore.java +++ b/core/spi/src/main/java/org/apache/polygene/spi/entitystore/helpers/StateStore.java @@ -20,14 +20,13 @@ package org.apache.polygene.spi.entitystore.helpers; -import java.io.IOException; -import org.json.JSONObject; +import javax.json.JsonObject; +import org.apache.polygene.spi.entitystore.EntityStoreException; /** * StateStore SPI. */ public interface StateStore { - JSONObject jsonStateOf( String id ) - throws IOException; + JsonObject jsonStateOf( String id ) throws EntityStoreException; } http://git-wip-us.apache.org/repos/asf/polygene-java/blob/7c2814ee/core/spi/src/main/java/org/apache/polygene/spi/module/ModuleSpi.java ---------------------------------------------------------------------- diff --git a/core/spi/src/main/java/org/apache/polygene/spi/module/ModuleSpi.java b/core/spi/src/main/java/org/apache/polygene/spi/module/ModuleSpi.java index 8aa7c0d..eccc612 100644 --- a/core/spi/src/main/java/org/apache/polygene/spi/module/ModuleSpi.java +++ b/core/spi/src/main/java/org/apache/polygene/spi/module/ModuleSpi.java @@ -21,10 +21,10 @@ package org.apache.polygene.spi.module; import org.apache.polygene.api.identity.IdentityGenerator; import org.apache.polygene.api.metrics.MetricsProvider; +import org.apache.polygene.api.serialization.Serialization; import org.apache.polygene.api.structure.Module; -import org.apache.polygene.api.structure.TypeLookup; -import org.apache.polygene.api.value.ValueSerialization; import org.apache.polygene.spi.entitystore.EntityStore; +import org.apache.polygene.spi.type.ValueTypeFactory; public interface ModuleSpi extends Module { @@ -32,9 +32,9 @@ public interface ModuleSpi extends Module IdentityGenerator identityGenerator(); - ValueSerialization valueSerialization(); - - TypeLookup typeLookup(); + Serialization serialization(); MetricsProvider metricsProvider(); + + ValueTypeFactory valueTypeFactory(); } http://git-wip-us.apache.org/repos/asf/polygene-java/blob/7c2814ee/core/spi/src/main/java/org/apache/polygene/spi/serialization/AbstractBinaryDeserializer.java ---------------------------------------------------------------------- diff --git a/core/spi/src/main/java/org/apache/polygene/spi/serialization/AbstractBinaryDeserializer.java b/core/spi/src/main/java/org/apache/polygene/spi/serialization/AbstractBinaryDeserializer.java new file mode 100644 index 0000000..a8f2c2c --- /dev/null +++ b/core/spi/src/main/java/org/apache/polygene/spi/serialization/AbstractBinaryDeserializer.java @@ -0,0 +1,48 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.polygene.spi.serialization; + +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.Reader; +import java.io.UncheckedIOException; +import java.util.Base64; +import org.apache.polygene.api.structure.ModuleDescriptor; +import org.apache.polygene.api.type.ValueType; + +import static java.util.stream.Collectors.joining; + +public abstract class AbstractBinaryDeserializer extends AbstractDeserializer +{ + @Override + public <T> T deserialize( ModuleDescriptor module, ValueType valueType, Reader state ) + { + String stateString; + try( BufferedReader buffer = new BufferedReader( state ) ) + { + stateString = buffer.lines().collect( joining( "\n" ) ); + } + catch( IOException ex ) + { + throw new UncheckedIOException( ex ); + } + byte[] decoded = Base64.getDecoder().decode( stateString ); + return deserialize( module, valueType, new ByteArrayInputStream( decoded ) ); + } +} http://git-wip-us.apache.org/repos/asf/polygene-java/blob/7c2814ee/core/spi/src/main/java/org/apache/polygene/spi/serialization/AbstractBinarySerializer.java ---------------------------------------------------------------------- diff --git a/core/spi/src/main/java/org/apache/polygene/spi/serialization/AbstractBinarySerializer.java b/core/spi/src/main/java/org/apache/polygene/spi/serialization/AbstractBinarySerializer.java new file mode 100644 index 0000000..e673ad7 --- /dev/null +++ b/core/spi/src/main/java/org/apache/polygene/spi/serialization/AbstractBinarySerializer.java @@ -0,0 +1,51 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.polygene.spi.serialization; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.io.Writer; +import java.util.Base64; +import org.apache.polygene.api.common.Optional; + +import static java.nio.charset.StandardCharsets.UTF_8; + +/** + * Base Binary Serializer. + * + * Implementations work on bytes, this base serializer encode these bytes in Base64 to produce Strings. + */ +public abstract class AbstractBinarySerializer extends AbstractSerializer +{ + @Override + public void serialize( Options options, Writer writer, @Optional Object object ) + { + try + { + ByteArrayOutputStream output = new ByteArrayOutputStream(); + serialize( options, output, object ); + byte[] base64 = Base64.getEncoder().encode( output.toByteArray() ); + writer.write( new String( base64, UTF_8 ) ); + } + catch( IOException ex ) + { + throw new UncheckedIOException( ex ); + } + } +} http://git-wip-us.apache.org/repos/asf/polygene-java/blob/7c2814ee/core/spi/src/main/java/org/apache/polygene/spi/serialization/AbstractDeserializer.java ---------------------------------------------------------------------- diff --git a/core/spi/src/main/java/org/apache/polygene/spi/serialization/AbstractDeserializer.java b/core/spi/src/main/java/org/apache/polygene/spi/serialization/AbstractDeserializer.java new file mode 100644 index 0000000..17982f3 --- /dev/null +++ b/core/spi/src/main/java/org/apache/polygene/spi/serialization/AbstractDeserializer.java @@ -0,0 +1,155 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.polygene.spi.serialization; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.io.Reader; +import java.io.StringReader; +import java.util.function.Function; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; +import org.apache.polygene.api.entity.EntityReference; +import org.apache.polygene.api.serialization.Deserializer; +import org.apache.polygene.api.structure.ModuleDescriptor; +import org.apache.polygene.api.type.CollectionType; +import org.apache.polygene.api.type.MapType; +import org.apache.polygene.api.type.ValueType; +import org.apache.polygene.spi.module.ModuleSpi; + +public abstract class AbstractDeserializer implements Deserializer +{ + protected static final ValueType ENTITY_REF_LIST_VALUE_TYPE = CollectionType.listOf( EntityReference.class ); + protected static final ValueType ENTITY_REF_MAP_VALUE_TYPE = MapType.of( String.class, EntityReference.class ); + + @Override + public <T> T deserialize( ModuleDescriptor module, ValueType valueType, String state ) + { + return deserialize( module, valueType, new StringReader( state ) ); + } + + @Override + public <T> Function<String, T> deserializeFunction( ModuleDescriptor module, ValueType valueType ) + { + return state -> deserialize( module, valueType, new StringReader( state ) ); + } + + @Override + public <T> Stream<T> deserializeEach( ModuleDescriptor module, ValueType valueType, Iterable<String> states ) + { + return StreamSupport.stream( states.spliterator(), false ) + .map( state -> deserialize( module, valueType, new StringReader( state ) ) ); + } + + @Override + public <T> Stream<T> deserializeEach( ModuleDescriptor module, ValueType valueType, String... states ) + { + return Stream.of( states ).map( state -> deserialize( module, valueType, new StringReader( state ) ) ); + } + + @Override + public <T> T fromBytes( ModuleDescriptor module, ValueType valueType, byte[] bytes ) + { + return deserialize( module, valueType, new ByteArrayInputStream( bytes ) ); + } + + @Override + public <T> Function<byte[], T> fromBytesFunction( ModuleDescriptor module, ValueType valueType ) + { + return bytes -> deserialize( module, valueType, new ByteArrayInputStream( bytes ) ); + } + + @Override + public <T> Stream<T> fromBytesEach( ModuleDescriptor module, ValueType valueType, Iterable<byte[]> states ) + { + return StreamSupport.stream( states.spliterator(), false ) + .map( bytes -> deserialize( module, valueType, new ByteArrayInputStream( bytes ) ) ); + } + + @Override + public <T> Stream<T> fromBytesEach( ModuleDescriptor module, ValueType valueType, byte[]... states ) + { + return Stream.of( states ).map( bytes -> deserialize( module, valueType, new ByteArrayInputStream( bytes ) ) ); + } + + @Override + public <T> T deserialize( ModuleDescriptor module, Class<T> type, InputStream state ) + { + return deserialize( module, valueTypeOf( module, type ), state ); + } + + @Override + public <T> T deserialize( ModuleDescriptor module, Class<T> type, Reader state ) + { + return deserialize( module, valueTypeOf( module, type ), state ); + } + + @Override + public <T> T deserialize( ModuleDescriptor module, Class<T> type, String state ) + { + return deserialize( module, valueTypeOf( module, type ), state ); + } + + @Override + public <T> Function<String, T> deserializeFunction( ModuleDescriptor module, Class<T> type ) + { + return deserializeFunction( module, valueTypeOf( module, type ) ); + } + + @Override + public <T> Stream<T> deserializeEach( ModuleDescriptor module, Class<T> type, Iterable<String> states ) + { + return deserializeEach( module, valueTypeOf( module, type ), states ); + } + + @Override + public <T> Stream<T> deserializeEach( ModuleDescriptor module, Class<T> type, String... states ) + { + return deserializeEach( module, valueTypeOf( module, type ), states ); + } + + @Override + public <T> T fromBytes( ModuleDescriptor module, Class<T> type, byte[] bytes ) + { + return fromBytes( module, valueTypeOf( module, type ), bytes ); + } + + @Override + public <T> Function<byte[], T> fromBytesFunction( ModuleDescriptor module, Class<T> type ) + { + return fromBytesFunction( module, valueTypeOf( module, type ) ); + } + + @Override + public <T> Stream<T> fromBytesEach( ModuleDescriptor module, Class<T> type, Iterable<byte[]> states ) + { + return fromBytesEach( module, valueTypeOf( module, type ), states ); + } + + @Override + public <T> Stream<T> fromBytesEach( ModuleDescriptor module, Class<T> type, byte[]... states ) + { + return fromBytesEach( module, valueTypeOf( module, type ), states ); + } + + private ValueType valueTypeOf( ModuleDescriptor module, Class<?> type ) + { + // TODO Remove (ModuleSpi) cast + return ( (ModuleSpi) module.instance() ).valueTypeFactory().valueTypeOf( module, type ); + } +} http://git-wip-us.apache.org/repos/asf/polygene-java/blob/7c2814ee/core/spi/src/main/java/org/apache/polygene/spi/serialization/AbstractSerializer.java ---------------------------------------------------------------------- diff --git a/core/spi/src/main/java/org/apache/polygene/spi/serialization/AbstractSerializer.java b/core/spi/src/main/java/org/apache/polygene/spi/serialization/AbstractSerializer.java new file mode 100644 index 0000000..b5f10ff --- /dev/null +++ b/core/spi/src/main/java/org/apache/polygene/spi/serialization/AbstractSerializer.java @@ -0,0 +1,147 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.polygene.spi.serialization; + +import java.io.ByteArrayOutputStream; +import java.io.OutputStream; +import java.io.StringWriter; +import java.io.Writer; +import java.util.function.Function; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; +import org.apache.polygene.api.common.Optional; +import org.apache.polygene.api.serialization.Serializer; + +public abstract class AbstractSerializer implements Serializer +{ + @Override + public void serialize( Writer writer, @Optional Object object ) + { + serialize( Options.DEFAULT, writer, object ); + } + + @Override + public void serialize( OutputStream output, @Optional Object object ) + { + serialize( Options.DEFAULT, output, object ); + } + + @Override + public String serialize( Options options, @Optional Object object ) + { + StringWriter writer = new StringWriter(); + serialize( options, writer, object ); + return writer.toString(); + } + + @Override + public String serialize( @Optional Object object ) + { + return serialize( Options.DEFAULT, object ); + } + + @Override + public <T> Function<T, String> serializeFunction( Options options ) + { + return object -> serialize( options, object ); + } + + @Override + public <T> Function<T, String> serializeFunction() + { + return object -> serialize( Options.DEFAULT, object ); + } + + @Override + public Stream<String> serializeEach( Options options, Iterable<Object> objects ) + { + return StreamSupport.stream( objects.spliterator(), false ) + .map( object -> serialize( options, object ) ); + } + + @Override + public Stream<String> serializeEach( Iterable<Object> objects ) + { + return StreamSupport.stream( objects.spliterator(), false ) + .map( object -> serialize( Options.DEFAULT, object ) ); + } + + @Override + public Stream<String> serializeEach( Options options, Object... objects ) + { + return Stream.of( objects ).map( object -> serialize( options, object ) ); + } + + @Override + public Stream<String> serializeEach( Object... objects ) + { + return Stream.of( objects ).map( object -> serialize( Options.DEFAULT, object ) ); + } + + @Override + public byte[] toBytes( Options options, @Optional Object object ) + { + ByteArrayOutputStream output = new ByteArrayOutputStream(); + serialize( options, output, object ); + return output.toByteArray(); + } + + @Override + public byte[] toBytes( @Optional Object object ) + { + return toBytes( Options.DEFAULT, object ); + } + + @Override + public <T> Function<T, byte[]> toBytesFunction( Options options ) + { + return object -> toBytes( options, object ); + } + + @Override + public <T> Function<T, byte[]> toBytesFunction() + { + return object -> toBytes( Options.DEFAULT, object ); + } + + @Override + public Stream<byte[]> toBytesEach( Options options, Iterable<Object> objects ) + { + return StreamSupport.stream( objects.spliterator(), false ) + .map( object -> toBytes( options, object ) ); + } + + @Override + public Stream<byte[]> toBytesEach( Iterable<Object> objects ) + { + return StreamSupport.stream( objects.spliterator(), false ) + .map( object -> toBytes( Options.DEFAULT, object ) ); + } + + @Override + public Stream<byte[]> toBytesEach( Options options, Object... objects ) + { + return Stream.of( objects ).map( object -> toBytes( options, object ) ); + } + + @Override + public Stream<byte[]> toBytesEach( Object... objects ) + { + return Stream.of( objects ).map( object -> toBytes( Options.DEFAULT, object ) ); + } +} http://git-wip-us.apache.org/repos/asf/polygene-java/blob/7c2814ee/core/spi/src/main/java/org/apache/polygene/spi/serialization/AbstractTextDeserializer.java ---------------------------------------------------------------------- diff --git a/core/spi/src/main/java/org/apache/polygene/spi/serialization/AbstractTextDeserializer.java b/core/spi/src/main/java/org/apache/polygene/spi/serialization/AbstractTextDeserializer.java new file mode 100644 index 0000000..0575489 --- /dev/null +++ b/core/spi/src/main/java/org/apache/polygene/spi/serialization/AbstractTextDeserializer.java @@ -0,0 +1,34 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.polygene.spi.serialization; + +import java.io.InputStream; +import java.io.InputStreamReader; +import org.apache.polygene.api.structure.ModuleDescriptor; +import org.apache.polygene.api.type.ValueType; + +import static java.nio.charset.StandardCharsets.UTF_8; + +public abstract class AbstractTextDeserializer extends AbstractDeserializer +{ + @Override + public <T> T deserialize( ModuleDescriptor module, ValueType valueType, InputStream state ) + { + return deserialize( module, valueType, new InputStreamReader( state, UTF_8 ) ); + } +} http://git-wip-us.apache.org/repos/asf/polygene-java/blob/7c2814ee/core/spi/src/main/java/org/apache/polygene/spi/serialization/AbstractTextSerializer.java ---------------------------------------------------------------------- diff --git a/core/spi/src/main/java/org/apache/polygene/spi/serialization/AbstractTextSerializer.java b/core/spi/src/main/java/org/apache/polygene/spi/serialization/AbstractTextSerializer.java new file mode 100644 index 0000000..3d8bb16 --- /dev/null +++ b/core/spi/src/main/java/org/apache/polygene/spi/serialization/AbstractTextSerializer.java @@ -0,0 +1,48 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.polygene.spi.serialization; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.StringWriter; +import java.io.UncheckedIOException; +import org.apache.polygene.api.common.Optional; + +import static java.nio.charset.StandardCharsets.UTF_8; + +/** + * Base Text Serializer. + * + * Implementations work on Strings, this base serializer encode these strings in UTF-8 to produce bytes. + */ +public abstract class AbstractTextSerializer extends AbstractSerializer +{ + public void serialize( Options options, OutputStream output, @Optional Object object ) + { + try + { + StringWriter writer = new StringWriter(); + serialize( options, writer, object ); + output.write( writer.toString().getBytes( UTF_8 ) ); + } + catch( IOException ex ) + { + throw new UncheckedIOException( ex ); + } + } +} http://git-wip-us.apache.org/repos/asf/polygene-java/blob/7c2814ee/core/spi/src/main/java/org/apache/polygene/spi/serialization/JsonDeserializer.java ---------------------------------------------------------------------- diff --git a/core/spi/src/main/java/org/apache/polygene/spi/serialization/JsonDeserializer.java b/core/spi/src/main/java/org/apache/polygene/spi/serialization/JsonDeserializer.java new file mode 100644 index 0000000..84c8993 --- /dev/null +++ b/core/spi/src/main/java/org/apache/polygene/spi/serialization/JsonDeserializer.java @@ -0,0 +1,164 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.polygene.spi.serialization; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.Reader; +import java.io.StringReader; +import java.io.UncheckedIOException; +import java.util.function.Function; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; +import javax.json.Json; +import javax.json.JsonObject; +import javax.json.JsonReader; +import javax.json.JsonValue; +import javax.json.stream.JsonParser; +import javax.json.stream.JsonParsingException; +import org.apache.polygene.api.serialization.Deserializer; +import org.apache.polygene.api.structure.ModuleDescriptor; +import org.apache.polygene.api.type.ValueType; +import org.apache.polygene.spi.module.ModuleSpi; + +import static java.util.stream.Collectors.joining; + +public interface JsonDeserializer extends Deserializer +{ + <T> T fromJson( ModuleDescriptor module, ValueType valueType, JsonValue state ); + + default <T> Function<JsonValue, T> fromJsonFunction( ModuleDescriptor module, ValueType valueType ) + { + return state -> fromJson( module, valueType, state ); + } + + default <T> Stream<T> fromJsonEach( ModuleDescriptor module, ValueType valueType, Stream<JsonValue> states ) + { + return states.map( fromJsonFunction( module, valueType ) ); + } + + default <T> Stream<T> fromJsonEach( ModuleDescriptor module, ValueType valueType, Iterable<JsonValue> states ) + { + return fromJsonEach( module, valueType, StreamSupport.stream( states.spliterator(), false ) ); + } + + default <T> Stream<T> fromJsonEach( ModuleDescriptor module, ValueType valueType, JsonValue... states ) + { + return fromJsonEach( module, valueType, Stream.of( states ) ); + } + + default <T> T fromJson( ModuleDescriptor module, Class<T> type, JsonValue state ) + { + // TODO Remove (ModuleSpi) cast + ValueType valueType = ( (ModuleSpi) module.instance() ).valueTypeFactory().valueTypeOf( module, type ); + return fromJson( module, valueType, state ); + } + + default <T> Function<JsonValue, T> fromJson( ModuleDescriptor module, Class<T> type ) + { + return state -> fromJson( module, type, state ); + } + + default <T> Stream<T> fromJsonEach( ModuleDescriptor module, Class<T> valueType, Stream<JsonValue> states ) + { + return states.map( fromJson( module, valueType ) ); + } + + default <T> Stream<T> fromJsonEach( ModuleDescriptor module, Class<T> valueType, Iterable<JsonValue> states ) + { + return fromJsonEach( module, valueType, StreamSupport.stream( states.spliterator(), false ) ); + } + + default <T> Stream<T> fromJsonEach( ModuleDescriptor module, Class<T> valueType, JsonValue... states ) + { + return fromJsonEach( module, valueType, Stream.of( states ) ); + } + + @Override + default <T> T deserialize( ModuleDescriptor module, ValueType valueType, Reader state ) + { + // JSR-353 Does not allow reading "out of structure" values + // See https://www.jcp.org/en/jsr/detail?id=353 + // And commented JsonReader#readValue() method in the javax.json API + // BUT, it will be part of the JsonReader contract in the next version + // See https://www.jcp.org/en/jsr/detail?id=374 + // Implementation by provider is optional though, so we'll always need a default implementation here. + // Fortunately, JsonParser has new methods allowing to read structures while parsing so it will be easy to do. + // In the meantime, a poor man's implementation reading the json into memory will do. + // TODO Revisit values out of structure JSON deserialization when JSR-374 is out + String stateString; + try( BufferedReader buffer = new BufferedReader( state ) ) + { + stateString = buffer.lines().collect( joining( "\n" ) ); + } + catch( IOException ex ) + { + throw new UncheckedIOException( ex ); + } + // We want plain Strings, BigDecimals, BigIntegers to be deserialized even when unquoted + Function<String, T> plainValueFunction = string -> + { + String poorMans = "{\"value\":" + string + "}"; + JsonObject poorMansJson = Json.createReader( new StringReader( poorMans ) ).readObject(); + JsonValue value = poorMansJson.get( "value" ); + return fromJson( module, valueType, value ); + }; + Function<String, T> outOfStructureFunction = string -> + { + // Is this an unquoted plain value? + try + { + return plainValueFunction.apply( '"' + string + '"' ); + } + catch( JsonParsingException ex ) + { + return plainValueFunction.apply( string ); + } + }; + try( JsonParser parser = Json.createParser( new StringReader( stateString ) ) ) + { + if( parser.hasNext() ) + { + JsonParser.Event e = parser.next(); + switch( e ) + { + case VALUE_NULL: + return null; + case START_ARRAY: + case START_OBJECT: + // JSON Structure + try( JsonReader reader = Json.createReader( new StringReader( stateString ) ) ) + { + return fromJson( module, valueType, reader.read() ); + } + default: + // JSON Value out of structure + return outOfStructureFunction.apply( stateString ); + } + } + } + catch( JsonParsingException ex ) + { + return outOfStructureFunction.apply( stateString ); + } + // Empty state string? + JsonValue emptyJsonString = Json.createReader( new StringReader( "{\"empty\":\"\"}" ) ) + .readObject().get( "empty" ); + return fromJson( module, valueType, emptyJsonString ); + } +} http://git-wip-us.apache.org/repos/asf/polygene-java/blob/7c2814ee/core/spi/src/main/java/org/apache/polygene/spi/serialization/JsonSerialization.java ---------------------------------------------------------------------- diff --git a/core/spi/src/main/java/org/apache/polygene/spi/serialization/JsonSerialization.java b/core/spi/src/main/java/org/apache/polygene/spi/serialization/JsonSerialization.java new file mode 100644 index 0000000..a98e70f --- /dev/null +++ b/core/spi/src/main/java/org/apache/polygene/spi/serialization/JsonSerialization.java @@ -0,0 +1,24 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.polygene.spi.serialization; + +import org.apache.polygene.api.serialization.Serialization; + +public interface JsonSerialization extends Serialization, JsonSerializer, JsonDeserializer +{ +} http://git-wip-us.apache.org/repos/asf/polygene-java/blob/7c2814ee/core/spi/src/main/java/org/apache/polygene/spi/serialization/JsonSerializer.java ---------------------------------------------------------------------- diff --git a/core/spi/src/main/java/org/apache/polygene/spi/serialization/JsonSerializer.java b/core/spi/src/main/java/org/apache/polygene/spi/serialization/JsonSerializer.java new file mode 100644 index 0000000..b64f240 --- /dev/null +++ b/core/spi/src/main/java/org/apache/polygene/spi/serialization/JsonSerializer.java @@ -0,0 +1,106 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.polygene.spi.serialization; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.io.Writer; +import java.util.function.Function; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; +import javax.json.JsonString; +import javax.json.JsonValue; +import org.apache.polygene.api.common.Optional; +import org.apache.polygene.api.serialization.Serializer; + +public interface JsonSerializer extends Serializer +{ + <T> Function<T, JsonValue> toJsonFunction( Options options ); + + default <T> Function<T, JsonValue> toJsonFunction() + { + return object -> toJsonFunction( Options.DEFAULT ).apply( object ); + } + + default JsonValue toJson( Options options, @Optional Object object ) + { + return toJsonFunction( options ).apply( object ); + } + + default JsonValue toJson( @Optional Object object ) + { + return toJsonFunction( Options.DEFAULT ).apply( object ); + } + + default <T> Stream<JsonValue> toJsonEach( Options options, Stream<T> objects ) + { + return objects.map( toJsonFunction( options ) ); + } + + default <T> Stream<JsonValue> toJsonEach( Options options, Iterable<T> objects ) + { + return toJsonEach( options, StreamSupport.stream( objects.spliterator(), false ) ); + } + + default <T> Stream<JsonValue> toJsonEach( Options options, Object... objects ) + { + return toJsonEach( options, Stream.of( objects ) ); + } + + default <T> Stream<JsonValue> toJsonEach( Stream<T> objects ) + { + return objects.map( toJsonFunction( Options.DEFAULT ) ); + } + + default <T> Stream<JsonValue> toJsonEach( Iterable<T> objects ) + { + return toJsonEach( Options.DEFAULT, StreamSupport.stream( objects.spliterator(), false ) ); + } + + default <T> Stream<JsonValue> toJsonEach( Object... objects ) + { + return toJsonEach( Options.DEFAULT, Stream.of( objects ) ); + } + + default void serialize( Options options, Writer writer, @Optional Object object ) + { + JsonValue jsonValue = toJson( options, object ); + if( jsonValue == null ) + { + return; + } + try + { + // TODO FIX ThIS SHIT of "out of structure" value (de)serialization + // We want plain Strings to be serialized without quotes which is non JSON compliant + // See https://java.net/jira/browse/JSON_PROCESSING_SPEC-65 + if( jsonValue.getValueType() == JsonValue.ValueType.STRING ) + { + writer.write( ( (JsonString) jsonValue ).getString() ); + } + else + { + writer.write( jsonValue.toString() ); + } + } + catch( IOException ex ) + { + throw new UncheckedIOException( ex ); + } + } +} http://git-wip-us.apache.org/repos/asf/polygene-java/blob/7c2814ee/core/spi/src/main/java/org/apache/polygene/spi/serialization/XmlDeserializer.java ---------------------------------------------------------------------- diff --git a/core/spi/src/main/java/org/apache/polygene/spi/serialization/XmlDeserializer.java b/core/spi/src/main/java/org/apache/polygene/spi/serialization/XmlDeserializer.java new file mode 100644 index 0000000..c7ac42b --- /dev/null +++ b/core/spi/src/main/java/org/apache/polygene/spi/serialization/XmlDeserializer.java @@ -0,0 +1,107 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.polygene.spi.serialization; + +import java.io.IOException; +import java.io.Reader; +import java.util.function.Function; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import org.apache.polygene.api.serialization.Deserializer; +import org.apache.polygene.api.serialization.SerializationException; +import org.apache.polygene.api.structure.ModuleDescriptor; +import org.apache.polygene.api.type.CollectionType; +import org.apache.polygene.api.type.EnumType; +import org.apache.polygene.api.type.MapType; +import org.apache.polygene.api.type.ValueCompositeType; +import org.apache.polygene.api.type.ValueType; +import org.apache.polygene.api.value.ValueDescriptor; +import org.apache.polygene.spi.module.ModuleSpi; +import org.w3c.dom.Document; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; + +public interface XmlDeserializer extends Deserializer +{ + <T> T fromXml( ModuleDescriptor module, ValueType valueType, Document state ); + + default <T> Function<Document, T> fromXmlFunction( ModuleDescriptor module, ValueType valueType ) + { + return state -> fromXml( module, valueType, state ); + } + + default <T> Stream<T> fromXmlEach( ModuleDescriptor module, ValueType valueType, Stream<Document> states ) + { + return states.map( fromXmlFunction( module, valueType ) ); + } + + default <T> Stream<T> fromXmlEach( ModuleDescriptor module, ValueType valueType, Iterable<Document> states ) + { + return fromXmlEach( module, valueType, StreamSupport.stream( states.spliterator(), false ) ); + } + + default <T> Stream<T> fromXmlEach( ModuleDescriptor module, ValueType valueType, Document... states ) + { + return fromXmlEach( module, valueType, Stream.of( states ) ); + } + + default <T> T fromXml( ModuleDescriptor module, Class<T> type, Document state ) + { + // TODO Remove (ModuleSpi) cast + ValueType valueType = ( (ModuleSpi) module.instance() ).valueTypeFactory().valueTypeOf( module, type ); + return fromXml( module, valueType, state ); + } + + default <T> Function<Document, T> fromXml( ModuleDescriptor module, Class<T> type ) + { + return state -> fromXml( module, type, state ); + } + + default <T> Stream<T> fromXmlEach( ModuleDescriptor module, Class<T> valueType, Stream<Document> states ) + { + return states.map( fromXml( module, valueType ) ); + } + + default <T> Stream<T> fromXmlEach( ModuleDescriptor module, Class<T> valueType, Iterable<Document> states ) + { + return fromXmlEach( module, valueType, StreamSupport.stream( states.spliterator(), false ) ); + } + + default <T> Stream<T> fromXmlEach( ModuleDescriptor module, Class<T> valueType, Document... states ) + { + return fromXmlEach( module, valueType, Stream.of( states ) ); + } + + @Override + default <T> T deserialize( ModuleDescriptor module, ValueType valueType, Reader state ) + { + try + { + DocumentBuilder docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); + Document doc = docBuilder.parse( new InputSource( state ) ); + return fromXml( module, valueType, doc ); + } + catch( SAXException | IOException | ParserConfigurationException ex ) + { + throw new SerializationException( "Unable to read XML document", ex ); + } + } +} http://git-wip-us.apache.org/repos/asf/polygene-java/blob/7c2814ee/core/spi/src/main/java/org/apache/polygene/spi/serialization/XmlSerialization.java ---------------------------------------------------------------------- diff --git a/core/spi/src/main/java/org/apache/polygene/spi/serialization/XmlSerialization.java b/core/spi/src/main/java/org/apache/polygene/spi/serialization/XmlSerialization.java new file mode 100644 index 0000000..12fda54 --- /dev/null +++ b/core/spi/src/main/java/org/apache/polygene/spi/serialization/XmlSerialization.java @@ -0,0 +1,24 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.polygene.spi.serialization; + +import org.apache.polygene.api.serialization.Serialization; + +public interface XmlSerialization extends Serialization, XmlSerializer, XmlDeserializer +{ +} http://git-wip-us.apache.org/repos/asf/polygene-java/blob/7c2814ee/core/spi/src/main/java/org/apache/polygene/spi/serialization/XmlSerializer.java ---------------------------------------------------------------------- diff --git a/core/spi/src/main/java/org/apache/polygene/spi/serialization/XmlSerializer.java b/core/spi/src/main/java/org/apache/polygene/spi/serialization/XmlSerializer.java new file mode 100644 index 0000000..32ce539 --- /dev/null +++ b/core/spi/src/main/java/org/apache/polygene/spi/serialization/XmlSerializer.java @@ -0,0 +1,123 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.polygene.spi.serialization; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.io.Writer; +import java.util.function.Function; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; +import javax.xml.transform.OutputKeys; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; +import org.apache.polygene.api.common.Optional; +import org.apache.polygene.api.serialization.SerializationException; +import org.apache.polygene.api.serialization.Serializer; +import org.w3c.dom.Document; +import org.w3c.dom.Node; + +/** + * XML State Serializer. + */ +public interface XmlSerializer extends Serializer +{ + <T> Function<T, Document> toXmlFunction( Options options ); + + default <T> Function<T, Document> toXmlFunction() + { + return object -> toXmlFunction( Options.DEFAULT ).apply( object ); + } + + default Document toXml( Options options, @Optional Object object ) + { + return toXmlFunction( options ).apply( object ); + } + + default Document toXml( @Optional Object object ) + { + return toXmlFunction( Options.DEFAULT ).apply( object ); + } + + default <T> Stream<Document> toXmlEach( Options options, Stream<T> objects ) + { + return objects.map( toXmlFunction( options ) ); + } + + default <T> Stream<Document> toXmlEach( Options options, Iterable<T> objects ) + { + return toXmlEach( options, StreamSupport.stream( objects.spliterator(), false ) ); + } + + default <T> Stream<Document> toXmlEach( Options options, Object... objects ) + { + return toXmlEach( options, Stream.of( objects ) ); + } + + default <T> Stream<Document> toXmlEach( Stream<T> objects ) + { + return objects.map( toXmlFunction( Options.DEFAULT ) ); + } + + default <T> Stream<Document> toXmlEach( Iterable<T> objects ) + { + return toXmlEach( Options.DEFAULT, StreamSupport.stream( objects.spliterator(), false ) ); + } + + default <T> Stream<Document> toXmlEach( Object... objects ) + { + return toXmlEach( Options.DEFAULT, Stream.of( objects ) ); + } + + default void serialize( Options options, Writer writer, @Optional Object object ) + { + Document xmlDocument = toXml( options, object ); + if( xmlDocument == null ) + { + return; + } + try + { + // We want plain Strings to be serialized without quotes + if( xmlDocument.getNodeType() == Node.TEXT_NODE ) + { + writer.write( xmlDocument.getNodeValue() ); + } + else + { + Transformer transformer = TransformerFactory.newInstance().newTransformer(); + transformer.setOutputProperty( OutputKeys.METHOD, "xml" ); + transformer.setOutputProperty( OutputKeys.VERSION, "1.1" ); + transformer.setOutputProperty( OutputKeys.STANDALONE, "yes" ); + transformer.setOutputProperty( OutputKeys.ENCODING, "UTF-8" ); + transformer.transform( new DOMSource( xmlDocument ), new StreamResult( writer ) ); + } + } + catch( IOException ex ) + { + throw new UncheckedIOException( ex ); + } + catch( TransformerException ex ) + { + throw new SerializationException( "Unable to transform XML Document to String", ex ); + } + } +} http://git-wip-us.apache.org/repos/asf/polygene-java/blob/7c2814ee/core/spi/src/main/java/org/apache/polygene/spi/serialization/package.html ---------------------------------------------------------------------- diff --git a/core/spi/src/main/java/org/apache/polygene/spi/serialization/package.html b/core/spi/src/main/java/org/apache/polygene/spi/serialization/package.html new file mode 100644 index 0000000..2e2f188 --- /dev/null +++ b/core/spi/src/main/java/org/apache/polygene/spi/serialization/package.html @@ -0,0 +1,24 @@ +<!-- + ~ Licensed to the Apache Software Foundation (ASF) under one + ~ or more contributor license agreements. See the NOTICE file + ~ distributed with this work for additional information + ~ regarding copyright ownership. The ASF licenses this file + ~ to you under the Apache License, Version 2.0 (the + ~ "License"); you may not use this file except in compliance + ~ with the License. You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + ~ + ~ + --> +<html> + <body> + <h2>Serialization SPI.</h2> + </body> +</html>
