Spring Data Neo4j: Converter of object to string works, but object to long is not executed - spring

I have a really strange issue with converting from domain objects to those Neo4j can natively store as property value. As a test case I use Joda's DateTime. A object of that type can be converted to a String or Long quite easily.
The conversion from DateTime to String works flawlessly with this code:
public class DateTimeToStringConverter implements Converter<DateTime, String> {
#Override
public String convert(DateTime source) {
return source.toDateTimeISO().toString();
}
}
The property shows up in the node:
Node[1] {
'__type__' = '...',
'entityEditedAt' = '2012-12-28T12:32:50.308+01:00',
'entityCreatedAt' = '2012-12-28T12:32:50.297+01:00',
...
}
However if I like to save the DateTime as Long (useful to sort by time in Cypher), it does not work at all. Here is my converter:
public class DateTimeToLongConverter implements Converter<DateTime, Long> {
#Override
public Long convert(DateTime source) {
return source.toDateTimeISO().getMillis();
}
}
The property is not saved on the node. Thus it is missing completely. No exception is thrown. It seems like the conversion code is not called at all.
The converters are hooked to Spring Data using code based configuration:
#Bean
public ConversionServiceFactoryBean conversionService() {
Set converters = Sets.newHashSet();
// These work!
converters.add(new DateTimeToStringConverter());
converters.add(new StringToDateTimeConverter());
// These don't :-(
//converters.add(new DateTimeToLongConverter());
//converters.add(new LongToDateTimeConverter());
ConversionServiceFactoryBean bean = new ConversionServiceFactoryBean();
bean.setConverters(converters);
return bean;
}
Any clues? I'm quite lost here, as it should work in my opinion...
Edit
I found following text in the Spring Data Neo4j documentation:
All fields convertible to a String using the Spring conversion services will be stored as a string.
Does this mean, that only conversions to string are supported? This seems rather limiting.

Tell SDN that you want to store your joda DateTime property as a long with:
#NodeEntity
public class MyEntity {
...
#GraphProperty(propertyType = Long.class)
private DateTime timestamp;
....
}
Then your registered DateTimeToLongConverter will kick in.

Related

Making Date queries on MongoDB using JSON on SpringDataMongoDB

I'm having some trouble making MongoDB Date queries using #Query annotation on SpringDataMongoDB on a project created using JHipster.
Since JHipster was used to create the project most of the queries were created using Spring Data query builder mechanism and for more refined queries, instead of using Type-safe Query methods I decided to stick with JHipster's standard configuration and make personalized queries using #Query annotation that allows the creation of MongoDBJSON queries.
However, I can't reference in my Json queries any entity field of type Date or LocalDate.
I tried to adopt as a solution the answer from this thread without success.
Query attempts
#Repository
public interface CourseClassRepository extends MongoRepository<CourseClass, String> {
// WORKS - query with `endDate` directly constructed by Spring Data
// This sollution however isn't enought, since 'experience_enrollments.device_id' cannot be used as a parameter
List<CourseClass> findAllByInstitutionIdAndEndDateIsGreaterThanEqual(Long institutionId, LocalDate dateLimit);
// Using #Query to create a JSON query doesn't work.
// apparently data parameter cannot be found. This is weird, considering that in any other #Query created the parameter is found just fine.
// ERROR: org.bson.json.JsonParseException: Invalid JSON input. Position: 124. Character: '?'
#Query(" { 'experience_enrollments.device_id' : ?0, 'institution_id': ?1, 'end_date': { $gte: { $date: ?2 } } } ")
List<CourseClass> findAllByExperienceDeviceAndInstitutionIdAndEndDate(String deviceId, Long institutionId, Date dateLimit);
// Adopting the stackoverflow answer mentioned above also throws an error. I belive that this error is related to the fact that '?2' is being interpreted as a String value and not as reference to a parameter
// ERROR: org.bson.json.JsonParseException: Failed to parse string as a date
#Query(" { 'experience_enrollments.device_id' : ?0, 'institution_id': ?1, 'end_date': { $gte: { $date: '?2' } } } ")
List<CourseClass> findAllByExperienceDeviceAndInstitutionIdAndEndDate(String deviceId, Long institutionId, Date dateLimit);
// Even hardcoding the date parameter, the query throws an error
// ERROR: org.bson.codecs.configuration.CodecConfigurationException: Can't find a codec for class java.time.ZonedDateTime.
#Query(" { 'experience_enrollments.device_id' : ?0, 'institution_id': ?1, 'end_date': { '$gte': { '$date': '2015-05-16T07:55:23.257Z' } } }")
List<CourseClass> findAllByExperienceDeviceAndInstitutionIdAndEndDate(String deviceId, Long institutionId);
}
Database Configurations
#Configuration
#EnableMongoRepositories("br.com.pixinside.lms.course.repository")
#Profile("!" + JHipsterConstants.SPRING_PROFILE_CLOUD)
#Import(value = MongoAutoConfiguration.class)
#EnableMongoAuditing(auditorAwareRef = "springSecurityAuditorAware")
public class DatabaseConfiguration {
#Bean
public MongoCustomConversions customConversions() {
List<Converter<?, ?>> converters = new ArrayList<>();
converters.add(DateToZonedDateTimeConverter.INSTANCE);
converters.add(ZonedDateTimeToDateConverter.INSTANCE);
return new MongoCustomConversions(converters);
}
}
Date converters
public static class DateToZonedDateTimeConverter implements Converter<Date, ZonedDateTime> {
public static final DateToZonedDateTimeConverter INSTANCE = new DateToZonedDateTimeConverter();
private DateToZonedDateTimeConverter() {
}
#Override
public ZonedDateTime convert(Date source) {
return source == null ? null : ZonedDateTime.ofInstant(source.toInstant(), ZoneId.systemDefault());
}
}
public static class ZonedDateTimeToDateConverter implements Converter<ZonedDateTime, Date> {
public static final ZonedDateTimeToDateConverter INSTANCE = new ZonedDateTimeToDateConverter();
private ZonedDateTimeToDateConverter() {
}
#Override
public Date convert(ZonedDateTime source) {
return source == null ? null : Date.from(source.toInstant());
}
}
Turns out that, as mentioned by Christoph Strobl, the behavior was, in fact, a bug. So it won't be necessary to worry about that in a future version of Spring Data MongoDB. Until there, I'm sharing my solution.
Since I was unable to use MongoDBJSon to create the query, I used the MongoTemplate and everything was just fine.
import org.springframework.data.mongodb.core.MongoTemplate;
import static org.springframework.data.mongodb.core.query.Criteria.where;
import static org.springframework.data.mongodb.core.query.Query.query;
#Autowired
public MongoTemplate mongoTemplate;
public List<CourseClass> findEnrolledOnExperienceDeviceWithMaxEndDateAndInstitutionId(String deviceId, LocalDate endDate, Long institutionId) {
return mongoTemplate.find(query(
where("experience_enrollments.device_id").is(deviceId)
.and("institution_id").is(institutionId)
.and("end_date").gte(endDate)), CourseClass.class);
}

Jackson #JsonFilter is not getting applied when used at field or method level

I am using Spring version 4.3.3 and Jackson version 2.8.3. I am trying to filter out specific fields from an entity bean based on some custom logic that is determined at runtime. The #JsonFilter seems ideal for this type of functionality. The problem is that when I put it at the field or method level, my custom filter never gets invoked. If I put it at the class level, it gets invoked just fine. I don't want to use it at the class level though since then I would need to separately maintain the list of hardcoded field names that I want to apply the logic to. As of Jackson 2.3, the ability to put this annotation at the field level is supposed to exist.
Here is the most basic custom filter without any custom logic yet:
public class MyFilter extends SimpleBeanPropertyFilter {
#Override
protected boolean include(BeanPropertyWriter beanPropertyWriter) {
return true;
}
#Override
protected boolean include(PropertyWriter propertyWriter) {
return true;
}
}
Then I have the Jackson ObjectMapper configuration:
public class MyObjectMapper extends ObjectMapper {
public MyObjectMapper () {
SimpleFilterProvider filterProvider = new SimpleFilterProvider();
filterProvider.addFilter("myFilter", new MyFilter());
setFilterProvider(filterProvider);
}
}
Then finally I have my entity bean:
#Entity
public class Project implements Serializable {
private Long id;
private Long version;
#JsonFilter("myFilter") private String name;
#JsonFilter("myFilter") private String description;
// getters and setters
}
If I move the #JsonFilter annotation to the class level where #Entity is, the filter at least gets invoked, but when it is at the field level like in the example here, it never gets invoked.
I have the same need but after examining the unit tests I discovered that this is not the use-case covered by annotating a field.
Annotating a field invokes a filter on the value of the field not the instance containing the field. For example, imagine you have to classes, A and B, where A contains a field of type B.
class A {
#JsonFilter("myFilter") B foo;
}
Jackson applies "myFilter" to the fields in B not in A. Since your example contains fields of type String, which has no fields, Jackson never invokes your filter.
I have a need to exclude certain fields based on the caller's permissions. For example, an employee's profile may contain his taxpayer id, which is considered sensitive information and should only be serialized if the caller is a member of the Payrole department. Since I'm using Spring Security, I wish to integrate Jackson with the current security context.
public class EmployeeProfile {
private String givenName;
private String surname;
private String emailAddress;
#VisibleWhen("hasRole('PayroleSpecialist')")
private String taxpayerId;
}
The most obvious way to do this is to Jackson's filter mechanism but it has a few limitations:
Jackson does not support nested filters so adding an access filter prohibits using filters for any other purpose.
One cannot add Jackson annotations to existing, third-party classes.
Jackson filters are not designed to be generic. The intent is to write a custom filter for each class you wish to apply filtering. For example, I you need to filter classes A and B, then you have to write an AFilter and a BFilter.
For my use-case, the solution is to use a custom annotation introspector in conjunction with a chaining filter.
public class VisibilityAnnotationIntrospector extends JacksonAnnotationIntrospector {
private static final long serialVersionUID = 1L;
#Override
public Object findFilterId(Annotated a) {
Object result = super.findFilterId(a);
if (null != result) return result;
// By always returning a value, we cause Jackson to query the filter provider.
// A more sophisticated solution will introspect the annotated class and only
// return a value if the class contains annotated properties.
return a instanceof AnnotatedClass ? VisibilityFilterProvider.FILTER_ID : null;
}
}
This is basically a copy SimpleBeanProvider that replaces calls to include with calls to isVisible. I'll probably update this to use a Java 8 BiPredicate to make the solution more general but works for now.
This class also takes another filter as an argument and will delegate to it the final decision on whether to serialize the field if the field is visible.
public class AuthorizationFilter extends SimpleBeanPropertyFilter {
private final PropertyFilter antecedent;
public AuthorizationFilter() {
this(null);
}
public AuthorizationFilter(final PropertyFilter filter) {
this.antecedent = null != filter ? filter : serializeAll();
}
#Deprecated
#Override
public void serializeAsField(Object bean, JsonGenerator jgen, SerializerProvider provider, BeanPropertyWriter writer) throws Exception {
if (isVisible(bean, writer)) {
this.antecedent.serializeAsField(bean, jgen, provider, writer);
} else if (!jgen.canOmitFields()) { // since 2.3
writer.serializeAsOmittedField(bean, jgen, provider);
}
}
#Override
public void serializeAsField(Object pojo, JsonGenerator jgen, SerializerProvider provider, PropertyWriter writer) throws Exception {
if (isVisible(pojo, writer)) {
this.antecedent.serializeAsField(pojo, jgen, provider, writer);
} else if (!jgen.canOmitFields()) { // since 2.3
writer.serializeAsOmittedField(pojo, jgen, provider);
}
}
#Override
public void serializeAsElement(Object elementValue, JsonGenerator jgen, SerializerProvider provider, PropertyWriter writer) throws Exception {
if (isVisible(elementValue, writer)) {
this.antecedent.serializeAsElement(elementValue, jgen, provider, writer);
}
}
private static boolean isVisible(Object pojo, PropertyWriter writer) {
// Code to determine if the field should be serialized.
}
}
I then add a custom filter provider to each instance of ObjectMapper.
#SuppressWarnings("deprecation")
public class VisibilityFilterProvider extends SimpleFilterProvider {
private static final long serialVersionUID = 1L;
static final String FILTER_ID = "dummy-filter-id";
#Override
public BeanPropertyFilter findFilter(Object filterId) {
return super.findFilter(filterId);
}
#Override
public PropertyFilter findPropertyFilter(Object filterId, Object valueToFilter) {
if (FILTER_ID.equals(filterId)) {
// This implies that the class did not have an explict filter annotation.
return new AuthorizationFilter(null);
}
// The class has an explicit filter annotation so delegate to it.
final PropertyFilter antecedent = super.findPropertyFilter(filterId, valueToFilter);
return new VisibilityPropertyFilter(antecedent);
}
}
Finally, I have a Jackson module that automatically registers the custom annotaion introspector so I don't have to add it to each ObjectMapper instance manually.
public class FieldVisibilityModule extends SimpleModule {
private static final long serialVersionUID = 1L;
public FieldVisibilityModule() {
super(PackageVersion.VERSION);
}
#Override
public void setupModule(Module.SetupContext context) {
super.setupModule(context);
// Append after other introspectors (instead of before) since
// explicit annotations should have precedence
context.appendAnnotationIntrospector(new VisibilityAnnotationIntrospector());
}
}
There are more improvements that can be made and I still have more unit tests to write (e.g., handling arrays and collections) but this is the basic strategy I used.
You can try this approach for the same purpose:
#Entity
#Inheritance(
strategy = InheritanceType.SINGLE_TABLE
)
#DiscriminatorColumn(
discriminatorType = DiscriminatorType.STRING,
length = 2
)
#Table(
name = "project"
)
#JsonTypeInfo(
use = Id.CLASS,
include = As.PROPERTY,
property = "#class"
)
#JsonSubTypes({
#Type(
value = BasicProject.class,
name = "basicProject"
),
#Type(
value = AdvanceProject.class,
name = "advanceProject"
)})
public abstract class Project {
private Long id;
private Long version;
}
#Entity
#DiscriminatorValue("AD")
public class AdvanceProject extends Project {
private String name;
private String description;
}
#Entity
#DiscriminatorValue("BS")
public class BasicProject extends Project {
private String name;
}
I don't think you will make it work. I was trying and these are results of my investigation, maybe it will be helpful.
First of all, as #Faron noticed, the #JsonFilterannotation is applied for the class being annotated not a field.
Secondly, I see things this way. Let's imagine, somewhere in Jackson internals you are able to get the actual field. You can figure out if there is the annotation using Java Reflection API. You can even get the filter name. Then you get to the filter and pass the field value there. But it happens at runtime, how will you get the corresponding JsonSerializer of the field type if you decide to serialize the field? It is impossible because of type erasure.
The only alternative I see is to forget about dynamic logic. Then you can do the following things:
1) extend JacksonAnnotationIntrospector (almost the same as implement AnnotationIntrospector but no useless default code) overriding hasIgnoreMarker method. Take a look at this answer
2) criminal starts here. Kinda weird way taking into account your initial goal but still: extend BeanSerializerModifier and filter out fields there. An example can be found here. This way you can define serializer that actually doesn't serialize anything (again, I understand how strange it is but maybe one will find it helpful)
3) similar to the approach above: define useless serializer based on BeanDescription implementing ContextualSerializer's createContextual method. The example of this magic is here
Thanks to this really good blog, I was able to use #JsonView to filter out specific fields from an entity bean based on some custom logic that is determined at runtime.
Since the #JsonFilter does not apply for the fields within a class, I found this to be a cleaner workaround.
Here is the sample code:
#Data
#AllArgsConstructor
public class TestEntity {
private String a;
#JsonView(CustomViews.SecureAccess.class)
private Date b;
#JsonView(CustomViews.SecureAccess.class)
private Integer c;
private List<String> d;
}
public class CustomViews {
public static interface GeneralAccess {}
public static interface SecureAccess {}
public static class GeneralAccessClass implements GeneralAccess {}
public static class SecureAccessClass implements SecureAccess, GeneralAccess {}
public static Class getWriterView(final boolean hasSecureAccess) {
return hasSecureAccess
? SecureAccessClass.class
: GeneralAccessClass.class;
}
}
#Test
public void test() throws JsonProcessingException {
final boolean hasSecureAccess = false; // Custom logic resolved to a boolean value at runtime.
final TestEntity testEntity = new TestEntity("1", new Date(), 2, ImmutableList.of("3", "4", "5"));
final ObjectMapper objectMapper = new ObjectMapper().enable(MapperFeature.DEFAULT_VIEW_INCLUSION);
final String serializedValue = objectMapper
.writerWithView(CustomViews.getWriterView(hasSecureAccess))
.writeValueAsString(testEntity);
Assert.assertTrue(serializedValue.contains("a"));
Assert.assertFalse(serializedValue.contains("b"));
Assert.assertFalse(serializedValue.contains("c"));
Assert.assertTrue(serializedValue.contains("d"));
}

How to spring inject configuration value for Joda Period

How can I use the #Value annotation to configure a Joda-Time Period field in my spring bean?
E.g. Given the following component class:
#Component
public class MyService {
#Value("${myapp.period:P1D}")
private Period periodField;
...
}
I want to use standard ISO8601 format to define the period in a properties file.
I get this error:
Caused by: java.lang.IllegalStateException: Cannot convert value of type [java.lang.String] to required type [org.joda.time.Period]: no matching editors or conversion strategy found
at org.springframework.beans.TypeConverterDelegate.convertIfNecessary(TypeConverterDelegate.java:302)
at org.springframework.beans.TypeConverterDelegate.convertIfNecessary(TypeConverterDelegate.java:125)
at org.springframework.beans.TypeConverterSupport.doConvert(TypeConverterSupport.java:61)
... 35 more
A simple solution that does not require any java code is to use Spring Expression Language (SpEL).
(My example uses java.time.Duration and not Joda stuff but I think you get it anyway.)
#Value("#{T(java.time.Duration).parse('${notifications.maxJobAge}')}")
private Duration maxJobAge;
What you can do is register a Spring ConversionService bean and implement a proper converter.
#Bean
public ConversionServiceFactoryBean conversionService() {
ConversionServiceFactoryBean conversionServiceFactoryBean = new ConversionServiceFactoryBean();
Set<Converter<?, ?>> myConverters = new HashSet<>();
myConverters.add(new StringToPeriodConverter());
conversionServiceFactoryBean.setConverters(myConverters);
return conversionServiceFactoryBean;
}
public class StringToPeriodConverter implements Converter<String, Period> {
#Override
public Period convert(String source) {
return Period.parse(source);
}
}
Another, not elegant, option, is to use a String setter who invokes the parse method.
#Value("${myapp.period:P1D}")
public void setPeriodField(String periodField)
{
if (isBlank(periodField))
this.periodField= null;
this.periodField= Duration.parse(periodField);
}
For joda-time:2.10.13 and spring-boot:2.3.2.RELEASE next example (like in question) is worked:
#Value("${myapp.period:P1D}")
private Period periodField;
If you use java.time.Period, in addition, worked a simple period property format (org.springframework.boot.convert.PeriodStyle):
#Value("${myapp.period:1d}")
private Period periodField;

spring ldap 2.0.1 replacing deprecated OdmManager

It seems in Spring LDAP 2.x, that the OdmManager facility is deprecated, as most odm-like things can be done by ldapTemplate, which is true. But the OdmManager has the ability to inject a ConverterManager which can be told about your custom type conversions. What is the equivalent method for using ldapTemplate for odm-like (ConverterManager) operations ?
If there is not an equivalent system in ldapTemplate, should it :
implicitly detect custom classes with single string constructors and String toString() class methods, iff they exist as properties to be mapped to/from ldap attributes.
implicitly allow the use of bean editors, to map from text to the specific type
explicitly have some facility like a Converter manager, in which you can configure this.
As an example, consider the simple class (which i would like to be the type of a bean property, which will be mapped to a ldap timestamp)
public class LdapTimestamp {
static private Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("Australia/Brisbane"));
static private DateFormat toStringFormat;
static {
toStringFormat = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT);
toStringFormat.setCalendar(cal);
}
static private DateFormat nativeLdapFormat = new SimpleDateFormat("yyyyMMddHHmmssZ");
private Date dateTime; // GMT time
public LdapTimestamp(String ldapDateTimeString) throws ParseException {
this.dateTime = nativeLdapFormat.parse(ldapDateTimeString);
}
public LdapTimestamp() {
super();
}
public Date getDateTime() {
return dateTime;
}
public void setDateTime(Date dateTimeObject) {
this.dateTime = dateTimeObject;
}
public void setDateTime(String ldapDateTimeString) throws ParseException {
this.dateTime = nativeLdapFormat.parse(ldapDateTimeString);
}
public String toStringShort() {
return toStringFormat.format(dateTime);
}
public String toString() {
return nativeLdapFormat.format(dateTime);
}
}
The intent is that the bean natively store a Date object, which can be used for date range comparisons and the like, while returning the ldap date string outwards of the bean as toString() and inward to the bean, as constructor with a single String argument.
This seems to be what is suggested with ConverterManager, but this is new code, so i'd rather not use the deprecated OdmManager interface if i can avoid it. ConverterManager is not deprecated, but i can't see an obvious way of linking it to ldapTemplate to use.
Any thoughts would be welcome.
LdapTemplate has a setObjectDirectoryMapper method, which enables you to inject a configured ObjectDirectoryMapper (which corresponds to the OdmManager in previous versions). DefaultObjectDirectoryMapper can be configured with a ConverterManager, so I think you should be all set.

How do you handle deserializing empty string into an Enum?

I am trying to submit a form from Ext JS 4 to a Spring 3 Controller using JSON. I am using Jackson 1.9.8 for the serialization/deserialization using Spring's built-in Jackson JSON support.
I have a status field that is initially null in the Domain object for a new record. When the form is submitted it generates the following json (scaled down to a few fields)
{"id":0,"name":"someName","status":""}
After submitted the following is seen in the server log
"nested exception is org.codehaus.jackson.map.JsonMappingException: Can not construct instance of com.blah.domain.StatusEnum from String value '': value not one of the declared Enum instance names"
So it appears that Jackson is expecting a valid Enum value or no value at all including an empty string. How do I fix this whether it is in Ext JS, Jackson or Spring?
I tried to create my own ObjectMapper such as
public class MyObjectMapper extends Object Mapper {
public MyObjectMapper() {
configure(DeserializationConfig.Feature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT, true);
}
}
and send this as a property to MappingJacksonMappingView but this didn't work. I also tried sending it in to MappingJacksonHttpMessageConverter but that didn't work. Side question: Which one should I be sending in my own ObjectMapper?
Suggestions?
The other thing you could do is create a specialized deserializer (extends org.codehaus.jackson.map.JsonDeserializer) for your particular enum, that has default values for things that don't match. What I've done is to create an abstract deserializer for enums that takes the class it deserializes, and it speeds this process along when I run into the issue.
public abstract class EnumDeserializer<T extends Enum<T>> extends JsonDeserializer<T> {
private Class<T> enumClass;
public EnumDeserializer(final Class<T> iEnumClass) {
super();
enumClass = iEnumClass;
}
#Override
public T deserialize(final JsonParser jp,
final DeserializationContext ctxt) throws IOException, JsonProcessingException {
final String value = jp.getText();
for (final T enumValue : enumClass.getEnumConstants()) {
if (enumValue.name().equals(value)) {
return enumValue;
}
}
return null;
}
}
That's the generic class, basically just takes an enum class, iterates over the values of the enum and checks the next token to match any name. If they do it returns it otherwise return null;
Then If you have an enum MyEnum you'd make a subclass of EnumDeserializer like this:
public class MyEnumDeserializer extends EnumDeserializer<MyEnum> {
public MyEnumDeserializer() {
super(MyEnum.class);
}
}
Then wherever you declare MyEnum:
#JsonDeserialize(using = MyEnumDeserializer.class)
public enum MyEnum {
...
}
I'm not familiar with Spring, but just in case, it may be easier to handle that on the client side:
Ext.define('My.form.Field', {
extend: 'Ext.form.field.Text',
getSubmitValue: function() {
var me = this,
value;
value = me.getRawValue();
if ( value === '' ) {
return ...;
}
}
});
You can also disallow submitting empty fields by setting their allowBlank property to false.
Ended up adding defaults in the EXT JS Model so there is always a value. Was hoping that I didn't have to this but it's not that big of a deal.
I have the same issue. I am reading a JSON stream with some empty strings. I am not in control of the JSON stream, because it is from a foreign service. And I am always getting the same error message. I tried this here:
mapper.getDeserializationConfig().with(DeserializationConfig.Feature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT);
But without any effect. Looks like a Bug.

Resources