how to include error page in spring MVC 3.0? - spring

how to include error page in spring MVC 3.0 ?
How show error page on exception occurrence .
<http auto-config="true">
<access-denied-handler ref="my403" />
<intercept-url pattern="/admin**" access="ROLE_ADMIN" />
</http>
<beans:bean id="my403" class="com.mkyong.web.exception.MyAccessDeniedHandler">
<beans:property name="errorPage" value="403" />
</beans:bean>

See either this thread on stackoverflow or work with #ControllerAdvice.
You can build your own error controller quite easy:
#ControllerAdvice
public class ExceptionHandlingController {
public static final String DEFAULT_ERROR_VIEW = "generalError";
final String NEW_LINE = System.getProperty("line.separator");
StringBuilder stackTraceString = new StringBuilder("");
#ExceptionHandler(Exception.class)
public ModelAndView handleError(HttpServletRequest req, Exception exception) {
//System.out.println("Request: " + req.getRequestURL() + " raised " + exception);
ModelAndView mav = new ModelAndView(DEFAULT_ERROR_VIEW);
mav.addObject("errorMessage", exception.getMessage());
for (StackTraceElement element : exception.getStackTrace() ){
stackTraceString.append( element );
stackTraceString.append( NEW_LINE );
}
mav.addObject("stackTrace", stackTraceString);
mav.addObject("url", req.getRequestURL());
return mav;
}
}

Related

AJAX get returns 404 in Spring

help me
index.jsp
$("#btn-submit").click(function () {
var username=document.getElementById("username");
var password=document.getElementById("password");
$.ajax({
url:"login",
contentType: 'application/json;charset=utf-8',
dataType: 'text',
headers: {'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')},
data: {
username:username.value,
password:password.value
},
type: 'get',
success: function (response) {
if (response=="1") {
alert(response);
}
else alert(response);
},
error: function (x, e) {
console.log(e)
}
});
});
LoginController.java
#RequestMapping("/login")
#Controller
public class LoginController {
#Autowired
private UserService userService;
#RequestMapping(value = { "/login" }, method = RequestMethod.GET)
#ResponseBody
public int checkValid(#RequestParam("username") String username,#RequestParam("password") String password, HttpServletRequest request, HttpServletResponse response, Locale locale, Model model){
try {
if (userService.findByUserName(username).equals(hashPass(password))){
return 1;
}
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
return 0;
}
return 0;
}
public String hashPass(String pass) throws NoSuchAlgorithmException {
MessageDigest md = MessageDigest.getInstance("SHA-256");
byte[] hashInBytes = md.digest(pass.getBytes(StandardCharsets.UTF_8));
// bytes to hex
StringBuilder sb = new StringBuilder();
for (byte b : hashInBytes) {
sb.append(String.format("%02x", b));
}
return sb.toString();
}
}
spring-config-servlet.xml
<?xml version="1.0" encoding="UTF-8"?>
http://www.springframework.org/schema/jdbc/spring-jdbc-4.3.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">
/WEB-INF/pages/
.jsp
/resources/jdbc.properties
<!-- Enable Annotation based Declarative Transaction Management -->
<tx:annotation-driven proxy-target-class="true"
transaction-manager="transactionManager" />
<!-- Creating TransactionManager Bean, since JDBC we are creating of type
DataSourceTransactionManager -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<bean id="postsDAO" class="com.blog.dao.impl.PostsDAO">
<property name="jdbcTemplate" ref="jdbcTemplate"/>
</bean>
<bean id="postsService" class="com.blog.service.impl.PostsService">
<property name="postsDAO" ref="postsDAO"/>
</bean>
<bean id="userDAO" class="com.blog.dao.impl.UserDAO">
<property name="jdbcTemplate" ref="jdbcTemplate"/>
</bean>
<bean id="userService" class="com.blog.service.impl.UserService">
<property name="userDAO" ref="userDAO"/>
</bean>
I use tomcat 9
Error:Failed to load resource: the server responded http://localhost:8080/Blog_war_exploded/login?username=root&password=root with a status of 404 ()
Look at your error: You are accessing http://localhost:8080/Blog_war_exploded/login but you actually want to access http://localhost:8080/login.
The reason is that you specified your URL as login instead of /login, so it is relative to the current "directory" and not to the root.
Changing the code to use /login should fix it:
$.ajax({
url: "/login",
...
})
On a side note, it's not a good idea to this via GET requests - among other things, the password will be stored in the server log in clear text. You should use a POST request instead.
Update:
Also, it seems you are use two request mappings for /login on top of each other, so you'll end up with /login/login. Check out how to use #RequestMapping properly.
Try changing the second (method-level) one to #RequestMapping(value = { "/" }, method = RequestMethod.GET) or just #RequestMapping("/").
I think the issue is related to your RequestMapping definition on both controller level and method level.
the first login at the controller level, means if you want to access any services in this controller, your requests have to start with "/login"
#RequestMapping("/login")
#Controller
public class LoginController {
and the second login at the method level, means you want to call the /login service under /login.
#RequestMapping(value = { "/login" }, method = RequestMethod.GET)
#ResponseBody
public int checkValid(#RequestParam("username") String username,#RequestParam("password") String password, HttpServletRequest request, HttpServletResponse response, Locale locale, Model model){
So the valid URL to call the /login service under /login controller is: /login/login
and because of this, your url /login was not found
you can either remove the first /login at the controller level, or use the /login/login from your ajax request...

How to pass object/json to post method using int-http:outbound-gateway in Spring Integration

How we can pass an object or json from Activator to int-http:outbound-gateway.
Below are my configs
<int:channel id="preparedData"/>
<int:service-activator input-channel="preparedData" ref="writer" method="writeData" output-channel="CallbackChannel">
</int:service-activator>
<int:channel id="CallbackChannel" />
<int-http:outbound-gateway
request-channel="CallbackChannel"
http-method="POST" url="{url}"
extract-request-payload="true">
<int-http:uri-variable name="url" expression="headers['url']" />
</int-http:outbound-gateway>
And my activator is returning one Object which is being expected in the POST API in one controller in #RequestBody
With above config getting below error.
13:58:41.202 [task-scheduler-1] ERROR org.springframework.integration.handler.LoggingHandler - org.springframework.messaging.MessageHandlingException: HTTP request execution failed for URI [http://**myUrl**]; nested exception is org.springframework.web.client.HttpClientErrorException: 400 Bad Request
Kindly suggest.
EDIT1
If I am converting my returned MyObject in JSON format in Activator then I am getting below error.
nested exception is java.lang.ClassCastException: java.lang.String cannot be cast to java.util.Map
Edit 2
When I changed my returntype of my Activator to Map with one of key value pair as 'input' and Object then it complains as below.
HTTP request execution failed for URI [http://MYURL]; nested exception is java.lang.ClassCastException: com.******.MyObject cannot be cast to java.lang.String
The <int-http:outbound-gateway> delegates all the hard work to the RestTemplate, which comes with this set of HttpMessageConverters by default:
this.messageConverters.add(new ByteArrayHttpMessageConverter());
this.messageConverters.add(new StringHttpMessageConverter());
this.messageConverters.add(new ResourceHttpMessageConverter());
this.messageConverters.add(new SourceHttpMessageConverter<>());
this.messageConverters.add(new AllEncompassingFormHttpMessageConverter());
if (romePresent) {
this.messageConverters.add(new AtomFeedHttpMessageConverter());
this.messageConverters.add(new RssChannelHttpMessageConverter());
}
if (jackson2XmlPresent) {
this.messageConverters.add(new MappingJackson2XmlHttpMessageConverter());
}
else if (jaxb2Present) {
this.messageConverters.add(new Jaxb2RootElementHttpMessageConverter());
}
if (jackson2Present) {
this.messageConverters.add(new MappingJackson2HttpMessageConverter());
}
else if (gsonPresent) {
this.messageConverters.add(new GsonHttpMessageConverter());
}
if (jackson2SmilePresent) {
this.messageConverters.add(new MappingJackson2SmileHttpMessageConverter());
}
if (jackson2CborPresent) {
this.messageConverters.add(new MappingJackson2CborHttpMessageConverter());
}
I think your Object is converted somehow to the wire bytes, you only should be sure that the proper and appropriate converted is used. And that is really depends of the server (#RequestBody) requirements.
Below is my solution that worked.
<int:channel id="preparedData"/>
<int:service-activator input-channel="preparedData" ref="writer" method="writeData" output-channel="CallbackChannel"/>
<int:channel id="CallbackChannel"/>
<int:transformer input-channel="CallbackChannel"
output-channel="registrationQueue"
ref="transformer" method="doTransform"/>
<int:channel id="registrationQueue" />
<int:header-enricher input-channel="registrationQueue" output-channel="enricherOutput">
<int:header name="contentType" value="application/json"/>
</int:header-enricher>
<int:channel id="enricherOutput" />
<int-http:outbound-gateway
request-channel="enricherOutput"
http-method="POST" url="{url}"
extract-request-payload="true"
reply-channel="replyChannel"
message-converters="converter"
<int-http:uri-variable name="url" expression="headers['url']" />
</int-http:outbound-gateway>
<util:list id="converter">
<bean id="test" class="com.xx.rr.ttt.MyMessageConvertor" />
</util:list>
<int:channel id="replyChannel" />
<int:service-activator input-channel="replyChannel" ref="lastActivator"/>
Had to add converter with below code.
public class MyMessageConvertor extends AbstractHttpMessageConverter<MyMessageConvertor> {
public static final Charset DEFAULT_CHARSET = Charset.forName("ISO-8859-1");
public MyMessageConvertor() {
this(DEFAULT_CHARSET);
}
public MyMessageConvertor(Charset defaultCharset) {
super(defaultCharset, new MediaType("application", "json"), MediaType.ALL);
}
#Override
protected boolean supports(Class<?> clazz) {
return true;
}
#Override
protected MyResponse readInternal(Class<? extends MyResponse > clazz,
HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
return null;
}
#Override
protected void writeInternal(MyResponse t, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException {
ObjectMapper mapperObj = new ObjectMapper();
String jsonStr = null;
try {
jsonStr = mapperObj.writeValueAsString(t);
} catch (IOException e) {
e.printStackTrace();
}
Charset charset = getContentTypeCharset(outputMessage.getHeaders().getContentType());
StreamUtils.copy(jsonStr, charset, outputMessage.getBody());
//System.out.println("outputMessage.getBody()" + outputMessage.getBody());
}
private Charset getContentTypeCharset(MediaType contentType) {
if (contentType != null && contentType.getCharset() != null) {
return contentType.getCharset();
}
else {
return getDefaultCharset();
}
}
}
I did not implement readInternal because the url that I am hitting is not returning anything but reply-channel in http:outbound-gateway is mandatory attribute thtswhy I had to add one and add one more activator which is finishing the flow.

Spring Security 3.2 - Adding Signup Form to Login Page

I have integrated Spring Security login successfuly to my wb application. However, I would like to have in the login page, in addition to login form, a signup form.
In order to do validation I need to pass to that form the object SignupForm which represents my form.
How can I do it? I have tried so many approaches and nothing works.
Please help....
ApplicationContext.xml
<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<property name="basenames">
<list><value>classpath:com/harryfwong/config/messages</value></list>
</property>
</bean>
message.properties in com.harryfwong.config resource package
username.required=Username is required
password.required=Password is required
passwordconfirm.mustmatch=Password must match
email.required=Email is required
Spring Validator
<!-- language: java -->
public class SignUpFormValidator implements Validator {
#Override
public boolean supports(Class<?> clazz) {
return SignUpForm.class.equals(clazz);
}
#Override
public void validate(Object target, Errors errors) {
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "username", "username.required");
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "password", "password.required");
SignUpForm form = (SignUpForm) target;
if (!StringUtils.isBlank(form.getPassword())) {
if (!form.getPassword().equals(form.getPasswordConfirm())) {
errors.rejectValue("passwordConfirm", "passwordconfirm.mustmatch");
}
}
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "email", "email.required");
}
}
Controller
<!-- language: java -->
#RequestMapping(method=RequestMethod.GET, value="signup")
public String signup(#ModelAttribute("form") SignUpForm form) {
if (form == null) {
form = new SignUpForm();
}
return "auth/signup";
}
#RequestMapping(method=RequestMethod.POST, value="signup")
public String signupSuccess(#ModelAttribute("form") SignUpForm form, BindingResult result, HttpServletRequest request) {
SignUpFormValidator suValidator = new SignUpFormValidator();
suValidator.validate(form, result);
if (result.hasErrors()) {
return signup(form);
} else {
authenticateUserAndSetSession(form, request);
return "auth/signupSuccess";
}
}
External Reference Login after signup: Auto login after successful registration
What does your SS configuration currently look like?
<form-login
login-page="/login"
login-processing-url="/loginProcess"
default-target-url="/landing"
always-use-default-target="false" />
Where /login is the request mapping
#RequestMapping(value="login")
public String login() {
// do something prior to display form below
return "auth/login";
}
auth/login.jsp
<form name='f' action="<c:url value='/loginProcess' />"
method='POST'>
Not sure what your security context config looks like - this is a response to your issue about not getting to the signup page
<http use-expressions="true">
<intercept-url pattern="/login" access="permitAll"/>
<intercept-url pattern="/loggedout" access="permitAll"/>
<intercept-url pattern="/signup" access="permitAll"/>
<intercept-url pattern="/" access="hasRole('ROLE_USER')" />
<intercept-url pattern="/**" access="hasRole('ROLE_USER')" />
...
</http>

Session timeout and ViewExpiredException handling on JSF/PrimeFaces ajax request

I find this article to be useful for non-ajax request How to handle session expiration and ViewExpiredException in JSF 2?
but I can't make use of this when I am submitting using an AJAX call.
Suppose in a primefaces dialog, I am making a post request using AJAX and session has already timed out.
I see my page getting stuck.
How to fix this kind of scenario such that when I post using AJAX, I could redirect him to my view expired page and
then forward him to the login page similar to the solution in the link above?
JSF2/Primefaces/Glassfish
Exceptions which are thrown during ajax requests have by default totally no feedback in the client side. Only when you run Mojarra with project stage set to Development and use <f:ajax>, then you will get a bare JavaScript alert with the exception type and message. But other than that, and in PrimeFaces, there's by default no feedback at all. You can however see the exception in the server log and in the ajax response (in the webbrowser's developer toolset's "Network" section).
You need to implement a custom ExceptionHandler which does basically the following job when there's a ViewExpiredException in the queue:
String errorPageLocation = "/WEB-INF/errorpages/expired.xhtml";
context.setViewRoot(context.getApplication().getViewHandler().createView(context, errorPageLocation));
context.getPartialViewContext().setRenderAll(true);
context.renderResponse();
Alternatively, you could use the JSF utility library OmniFaces. It has a FullAjaxExceptionHandler for exactly this purpose (source code here, showcase demo here).
See also:
Why use a JSF ExceptionHandlerFactory instead of <error-page> redirection?
What is the correct way to deal with JSF 2.0 exceptions for AJAXified components?
A merge between the answer of #BalusC and this post, I solved my problem!
My ExceptionHandlerWrapper:
public class CustomExceptionHandler extends ExceptionHandlerWrapper {
private ExceptionHandler wrapped;
CustomExceptionHandler(ExceptionHandler exception) {
this.wrapped = exception;
}
#Override
public ExceptionHandler getWrapped() {
return wrapped;
}
#Override
public void handle() throws FacesException {
final Iterator<ExceptionQueuedEvent> i = getUnhandledExceptionQueuedEvents().iterator();
while (i.hasNext()) {
ExceptionQueuedEvent event = i.next();
ExceptionQueuedEventContext context
= (ExceptionQueuedEventContext) event.getSource();
// get the exception from context
Throwable t = context.getException();
final FacesContext fc = FacesContext.getCurrentInstance();
final Map<String, Object> requestMap = fc.getExternalContext().getRequestMap();
final NavigationHandler nav = fc.getApplication().getNavigationHandler();
//here you do what ever you want with exception
try {
//log error ?
//log.log(Level.SEVERE, "Critical Exception!", t);
if (t instanceof ViewExpiredException) {
requestMap.put("javax.servlet.error.message", "Session expired, try again!");
String errorPageLocation = "/erro.xhtml";
fc.setViewRoot(fc.getApplication().getViewHandler().createView(fc, errorPageLocation));
fc.getPartialViewContext().setRenderAll(true);
fc.renderResponse();
} else {
//redirect error page
requestMap.put("javax.servlet.error.message", t.getMessage());
nav.handleNavigation(fc, null, "/erro.xhtml");
}
fc.renderResponse();
// remove the comment below if you want to report the error in a jsf error message
//JsfUtil.addErrorMessage(t.getMessage());
} finally {
//remove it from queue
i.remove();
}
}
//parent hanle
getWrapped().handle();
}
}
My ExceptionHandlerFactory:
public class CustomExceptionHandlerFactory extends ExceptionHandlerFactory {
private ExceptionHandlerFactory parent;
// this injection handles jsf
public CustomExceptionHandlerFactory(ExceptionHandlerFactory parent) {
this.parent = parent;
}
#Override
public ExceptionHandler getExceptionHandler() {
ExceptionHandler handler = new CustomExceptionHandler(parent.getExceptionHandler());
return handler;
}
}
My faces-config.xml
<?xml version='1.0' encoding='UTF-8'?>
<faces-config version="2.2"
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-facesconfig_2_2.xsd">
<factory>
<exception-handler-factory>
your.package.here.CustomExceptionHandlerFactory
</exception-handler-factory>
</factory>
</faces-config>
I am using Mojarra 2.1.7 in Production mode with JBoss 7. After the session expires, AJAX calls return an error XML document. You can easily catch this error using the usual onerror handler of f:ajax.
<script type="text/javascript">
function showError(data) {
alert("An error happened");
console.log(data);
}
</script>
<h:commandLink action="...">
<f:ajax execute="..." render="..." onerror="showError"/>
</h:commandLink>
I have included this in my ViewExpiredExceptionHandler class and it worked fine for me in WAS
public void handle() throws FacesException {
FacesContext facesContext = FacesContext.getCurrentInstance();
for (Iterator<ExceptionQueuedEvent> iter = getUnhandledExceptionQueuedEvents()
.iterator(); iter.hasNext();) {
Throwable exception = iter.next().getContext().getException();
if (exception instanceof ViewExpiredException) {
final ExternalContext externalContext = facesContext
.getExternalContext();
try {
facesContext.setViewRoot(facesContext.getApplication()
.getViewHandler()
.createView(facesContext, "/Login.xhtml")); //Login.xhtml is the page to to be viewed. Better not to give /WEB-INF/Login.xhtml
externalContext.redirect("ibm_security_logout?logoutExitPage=/Login.xhtml"); // when browser back button is pressed after session timeout, I used this.
facesContext.getPartialViewContext().setRenderAll(true);
facesContext.renderResponse();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
iter.remove();
}
}
}
getWrapped().handle();
}
Hope this helps
I faced this problem, Requirement need to display a confirmation popup when user do any action after session gets timed out, my proposed solution was:
<security:http use-expressions="true" auto-config="true" entry-point-ref="authenticationEntryPoint">
<security:intercept-url pattern="/common/auth/**" access="permitAll" />
<security:intercept-url pattern="/javax.faces.resource/**" access="permitAll" />
<security:intercept-url pattern="/**/ *.*" access="hasRole('ROLE_ADMIN')" />
<security:form-login login-page="/common/auth/login.jsf" />
<!-- <security:remember-me key="secret" services-ref="rememberMeServices" /> -->
<security:logout invalidate-session="true" logout-success-url="/common/auth/login.jsf" />
</security:http>
<bean id="authenticationEntryPoint" class="com.x.y.MyRedirectEntryPoint" >
<property name="loginFormUrl" value="/common/auth/login.jsf"/>
</bean>
The MyRedirectEntryPoint should extends AuthenticationProcessingFilterEntryPoint and override commence method
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException)
throws IOException, ServletException {
boolean ajaxRedirect = request.getHeader("faces-request") != null
&& request.getHeader("faces-request").toLowerCase().indexOf("ajax") > -1;
if (ajaxRedirect) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication == null) {
response.sendError(403);
}
} else {
super.commence(request, response, authException);
}
}
Now you can simply bind a callback javascript function to catch the thrown 403 error and do what ever you want:
$(document).bind('ajaxError',
function(event, request, settings, exception){
if (request.status==403){
//do whatever you wanted may be show a popup or just redirect
window.location = '#{request.contextPath}/';
}
});
For me a simple client side javascript handler worked:
function handleAjaxExpired(xhr,status,args) {
// handler for "oncomplete" ajax callback
if ( xhr.responseXML.getElementsByTagName('error-name').length ) {
// "<error-name>" tag is present -> check for "view expired" exception
html = xhr.responseXML.getElementsByTagName('error-name')[0].innerHTML;
if ( html.indexOf('ViewExpiredException') > -1 ) {
// view expired exception thrown
// do something / reload page
if ( confirm('session expired -> reload page?') ) {
document.location.href=document.location.href;
}
}
}
}
This handler is called from "oncomplete" attribute in triggering UI element, e.g. here from a rowSelect event in a Primefaces datatable:
<p:ajax event="rowSelect" oncomplete="handleAjaxExpired(xhr,status,args)" />
Update: To avoid adding "oncomplete" attributes to every ajax-enabled element, this javascript code searches globally in all ajax responses for errors:
(function() {
// intercept all ajax requests
var origXHROpen = XMLHttpRequest.prototype.open;
XMLHttpRequest.prototype.open = function() {
this.addEventListener('load', function() {
handleAjaxExpired(this);
});
origXHROpen.apply(this, arguments);
};
})();
This code makes "oncomplete" attributes in PrimeFaces UI-elements obsolete.

How to retrieve pK using spring security

I implement this method of the UserDetailService interface,
public UserDetails loadUserByUsername(final String username)
throws UsernameNotFoundException, DataAccessException {
final EmailCredential userDetails = persistentEmailCredential
.getUniqueEmailCredential(username);
if (userDetails == null) {
throw new UsernameNotFoundException(username + "is not registered");
}
final HashSet<GrantedAuthority> authorities = new HashSet<GrantedAuthority>();
authorities.add(new GrantedAuthorityImpl("ROLE_USER"));
for (UserRole role:userDetails.getAccount().getRoles()) {
authorities.add(new GrantedAuthorityImpl(role.getRole()));
}
return new User(userDetails.getEmailAddress(), userDetails
.getPassword(), true, true, true, true,
authorities);
}
In the security context I do some thing like this:
<!-- Login Info -->
<form-login default-target-url='/dashboard.htm' login-page="/login.htm"
authentication-failure-url="/login.htm?authfailed=true"
always-use-default-target='false' />
<logout logout-success-url="/login.htm" invalidate-session="true" />
<remember-me user-service-ref="emailAccountService" key="fuellingsport" />
<session-management>
<concurrency-control max-sessions="1" />
</session-management>
</http>
Now I want to pop out the Pk of the logged in user.
How can I show it in my jsp pages?
Any idea?
I guess you are looking for something like this, which can be used in jsp.
<security:authentication property="principal.username"/>
You can implement your own class extending User with required fields and return it from UserDetailsService.
Then you can access these fields as pointed out by Raghuram:
<security:authentication property="principal.pk"/>

Resources