Use references in RepositoryTransition

- Refactor so that we can create RepositoryState in one place
  and then reference to it from JpaRepositoryTransition impls.
- New common top class BaseRepositoryEntity for entities
- Change RepositoryState and RepositoryTransition to be abstract
  classes instead of interfaces.
- New custom StateMachineJackson2RepositoryPopulatorFactoryBean and
  StateMachineJackson2ResourceReader handling json refs.
- Relates to #262
This commit is contained in:
Janne Valkealahti
2016-10-09 10:21:22 +01:00
parent c016fd6e06
commit c7951c5001
18 changed files with 354 additions and 85 deletions

View File

@@ -0,0 +1,28 @@
/*
* Copyright 2016 the original author or authors.
*
* Licensed 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.springframework.statemachine.data;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
/**
* Generic base class for all entity classes.
*
* @author Janne Valkealahti
*
*/
@JsonTypeInfo(use=JsonTypeInfo.Id.CLASS, include=JsonTypeInfo.As.PROPERTY, property="_class")
public abstract class BaseRepositoryEntity {
}

View File

@@ -18,59 +18,59 @@ package org.springframework.statemachine.data;
import java.util.Set;
/**
* Generic interface representing state entity.
* Generic base class representing state entity.
*
* @author Janne Valkealahti
*
*/
public interface RepositoryState {
public abstract class RepositoryState extends BaseRepositoryEntity {
/**
* Gets the parent state.
*
* @return the parent state
*/
String getParentState();
public abstract String getParentState();
/**
* Gets the machine id.
*
* @return the machine id
*/
String getMachineId();
public abstract String getMachineId();
/**
* Gets the state.
*
* @return the state
*/
String getState();
public abstract String getState();
/**
* Checks if is initial.
*
* @return true, if is initial
*/
boolean isInitial();
public abstract boolean isInitial();
/**
* Gets the state actions.
*
* @return the state actions
*/
Set<? extends RepositoryAction> getStateActions();
public abstract Set<? extends RepositoryAction> getStateActions();
/**
* Gets the entry actions.
*
* @return the entry actions
*/
Set<? extends RepositoryAction> getEntryActions();
public abstract Set<? extends RepositoryAction> getEntryActions();
/**
* Gets the exit actions.
*
* @return the exit actions
*/
Set<? extends RepositoryAction> getExitActions();
public abstract Set<? extends RepositoryAction> getExitActions();
}

View File

@@ -175,8 +175,7 @@ public class RepositoryStateMachineModelFactory extends AbstractStateMachineMode
guard = new SpelExpressionGuard<>(parser.parseExpression(repositoryGuard.getSpel()));
}
}
transitionData.add(new TransitionData<>(t.getSource(), t.getTarget(), t.getEvent(), actions, guard, kind != null ? kind : TransitionKind.EXTERNAL));
transitionData.add(new TransitionData<>(t.getSource().getState(), t.getTarget().getState(), t.getEvent(), actions, guard, kind != null ? kind : TransitionKind.EXTERNAL));
}
TransitionsData<String, String> transitionsData = new TransitionsData<>(transitionData);

View File

@@ -20,59 +20,59 @@ import java.util.Set;
import org.springframework.statemachine.transition.TransitionKind;
/**
* Generic interface representing transition entity.
* Generic base class representing transition entity.
*
* @author Janne Valkealahti
*
*/
public interface RepositoryTransition {
public abstract class RepositoryTransition extends BaseRepositoryEntity {
/**
* Gets the machine id.
*
* @return the machine id
*/
String getMachineId();
public abstract String getMachineId();
/**
* Gets the source.
*
* @return the source
*/
String getSource();
public abstract RepositoryState getSource();
/**
* Gets the target.
*
* @return the target
*/
String getTarget();
public abstract RepositoryState getTarget();
/**
* Gets the event.
*
* @return the event
*/
String getEvent();
public abstract String getEvent();
/**
* Gets the actions.
*
* @return the actions
*/
Set<? extends RepositoryAction> getActions();
public abstract Set<? extends RepositoryAction> getActions();
/**
* Gets the guard.
*
* @return the guard
*/
RepositoryGuard getGuard();
public abstract RepositoryGuard getGuard();
/**
* Gets the transition kind.
*
* @return the transition kind
*/
TransitionKind getKind();
public abstract TransitionKind getKind();
}

View File

@@ -0,0 +1,48 @@
/*
* Copyright 2016 the original author or authors.
*
* Licensed 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.springframework.statemachine.data.support;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.data.repository.init.AbstractRepositoryPopulatorFactoryBean;
import org.springframework.data.repository.init.ResourceReader;
import org.springframework.data.repository.init.ResourceReaderRepositoryPopulator;
import com.fasterxml.jackson.databind.ObjectMapper;
/**
* {@link FactoryBean} to set up a {@link ResourceReaderRepositoryPopulator} with a {@link StateMachineJackson2ResourceReader}.
*
* @author Oliver Gierke
* @author Janne Valkealahti
*/
public class StateMachineJackson2RepositoryPopulatorFactoryBean extends AbstractRepositoryPopulatorFactoryBean {
private ObjectMapper mapper;
/**
* Configures the {@link ObjectMapper} to be used.
*
* @param mapper the new mapper
*/
public void setMapper(ObjectMapper mapper) {
this.mapper = mapper;
}
@Override
protected ResourceReader getResourceReader() {
return new StateMachineJackson2ResourceReader(mapper);
}
}

View File

@@ -0,0 +1,119 @@
/*
* Copyright 2016 the original author or authors.
*
* Licensed 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.springframework.statemachine.data.support;
import static com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.springframework.core.io.Resource;
import org.springframework.data.repository.init.Jackson2ResourceReader;
import org.springframework.data.repository.init.ResourceReader;
import org.springframework.statemachine.data.BaseRepositoryEntity;
import org.springframework.util.ClassUtils;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectReader;
/**
* A {@link ResourceReader} using Jackson to read JSON into objects.
*
* @author Oliver Gierke
* @author Janne Valkealahti
*/
public class StateMachineJackson2ResourceReader implements ResourceReader {
/** The Constant DEFAULT_TYPE_KEY. */
private static final String DEFAULT_TYPE_KEY = "_class";
/** The Constant DEFAULT_MAPPER. */
private static final ObjectMapper DEFAULT_MAPPER = new ObjectMapper();
static {
DEFAULT_MAPPER.configure(FAIL_ON_UNKNOWN_PROPERTIES, false);
}
/** The mapper. */
private final ObjectMapper mapper;
/** The type key. */
private String typeKey = DEFAULT_TYPE_KEY;
/**
* Creates a new {@link Jackson2ResourceReader}.
*/
public StateMachineJackson2ResourceReader() {
this(DEFAULT_MAPPER);
}
/**
* Creates a new {@link Jackson2ResourceReader} using the given {@link ObjectMapper}.
*
* @param mapper the mapper
*/
public StateMachineJackson2ResourceReader(ObjectMapper mapper) {
this.mapper = mapper == null ? DEFAULT_MAPPER : mapper;
}
/**
* Configures the JSON document's key to lookup the type to instantiate the object. Defaults to
* {@link Jackson2ResourceReader#DEFAULT_TYPE_KEY}.
*
* @param typeKey the new type key
*/
public void setTypeKey(String typeKey) {
this.typeKey = typeKey;
}
@Override
public Object readFrom(Resource resource, ClassLoader classLoader) throws Exception {
InputStream stream = resource.getInputStream();
ObjectReader objectReader = mapper.readerFor(JsonNode.class);
JsonNode node = objectReader.readTree(stream);
objectReader = mapper.readerFor(BaseRepositoryEntity[].class);
if (node.isArray()) {
List<Object> result = new ArrayList<Object>();
BaseRepositoryEntity[] entitys = objectReader.readValue(node);
result.addAll(Arrays.asList(entitys));
return result;
}
return readSingle(node, classLoader);
}
/**
* Reads the given {@link JsonNode} into an instance of the type encoded in it using the configured type key.
*
* @param node must not be {@literal null}.
* @param classLoader the class loader
* @return the object
* @throws IOException Signals that an I/O exception has occurred.
*/
private Object readSingle(JsonNode node, ClassLoader classLoader) throws IOException {
JsonNode typeNode = node.findValue(typeKey);
String typeName = typeNode == null ? null : typeNode.asText();
Class<?> type = ClassUtils.resolveClassName(typeName, classLoader);
return mapper.readerFor(type).readValue(node);
}
}