OpenStream throws IOException in AsyncTask - android

Context:
I'm developing an Android weather application in Java for a course. This application uses asynctask in order to get from OpenWeather's API the temperature of a city. The API returns a JSON with weather information through an URL like this:
http://api.openweathermap.org/data/2.5/weather?q={city name}&appid={key}
APPID is a key associated to an account in OpenWeather. Here is a JSON example for Berlin city:
{"coord":{"lon":13.39,"lat":52.52},"weather":[{"id":800,"main":"Clear","description":"clear sky","icon":"01d"}],"base":"stations","main":{"temp":8.49,"pressure":1035,"humidity":61,"temp_min":8,"temp_max":9},"visibility":10000,"wind":{"speed":6.7,"deg":120},"clouds":{"all":0},"dt":1542369000,"sys":{"type":1,"id":4892,"message":0.0027,"country":"DE","sunrise":1542349837,"sunset":1542381079},"id":2950159,"name":"Berlin","cod":200}
If the city doesn't exists, the API returns a JSON with error 404. The next one is a JSON example for the nonexistent city Berlina.
{"cod":"404","message":"city not found"}
I get the JSON with Asynctask. In doInBackground() method, I call the following line in order to get an inputStream:
InputStream inputStream = new URL(url).openStream();
Then, I parse the JSON in postOnExecute() and show the temperature information with a Toast.
My question:
The code works fine with Berlin, but openStream throws IOException for Berlina. The URL exists and have JSON data, as I explained. So, what could be the reasons for this Exception? Could it be a connection problem?
Edit with Logcat
This is the exception detail:
11-27 00:01:34.151 3152-3346/com.androidapps.user.weatherapp D/IOException: http://api.openweathermap.org/data/2.5/weather?q=Berlina&units=metric&appid=d32068a850405b593a87d410fcc50a4f
11-27 00:01:34.151 3152-3346/com.androidapps.user.weatherapp W/System.err: java.io.FileNotFoundException: http://api.openweathermap.org/data/2.5/weather?q=Berlina&units=metric&appid=d32068a850405b593a87d410fcc50a4f
11-27 00:01:34.152 3152-3346/com.androidapps.user.weatherapp W/System.err: at com.android.okhttp.internal.http.HttpURLConnectionImpl.getInputStream(HttpURLConnectionImpl.java:206)
11-27 00:01:34.152 3152-3346/com.androidapps.user.weatherapp W/System.err: at java.net.URL.openStream(URL.java:470)
11-27 00:01:34.152 3152-3346/com.androidapps.user.weatherapp W/System.err: at com.androidapps.user.weatherapp.MainActivity$GetJSONOpenWeather.doInBackground(MainActivity.java:219)
11-27 00:01:34.152 3152-3346/com.androidapps.user.weatherapp W/System.err: at com.androidapps.user.weatherapp.MainActivity$GetJSONOpenWeather.doInBackground(MainActivity.java:204)
11-27 00:01:34.152 3152-3346/com.androidapps.user.weatherapp W/System.err: at android.os.AsyncTask$2.call(AsyncTask.java:292)
11-27 00:01:34.152 3152-3346/com.androidapps.user.weatherapp W/System.err: at java.util.concurrent.FutureTask.run(FutureTask.java:237)
11-27 00:01:34.152 3152-3346/com.androidapps.user.weatherapp W/System.err: at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:231)
11-27 00:01:34.152 3152-3346/com.androidapps.user.weatherapp W/System.err: at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
11-27 00:01:34.152 3152-3346/com.androidapps.user.weatherapp W/System.err: at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
11-27 00:01:34.152 3152-3346/com.androidapps.user.weatherapp W/System.err: at java.lang.Thread.run(Thread.java:818)
11-27 00:01:34.192 3152-3180/com.androidapps.user.weatherapp D/EGL_emulation: eglMakeCurrent: 0xb42b9b20: ver 2 0
11-27 00:01:34.227 3152-3152/com.androidapps.user.weatherapp D/JSONObjetc: End of input at character 0 of

Related

Can not load data from webservice from the first time

After getting facebook access token, i use it for login in my own web server. However in the first time, there is warning in the log file, and i can not load data from my server, but when i open app from second times, it load normally.
Below is warning log:
W/System.err: com.google.gson.JsonSyntaxException:
java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING
at line 1 column 1 path $ W/System.err: at
com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:224)
W/System.err: at
retrofit2.converter.gson.GsonResponseBodyConverter.convert(GsonResponseBodyConverter.java:39)
W/System.err: at
retrofit2.converter.gson.GsonResponseBodyConverter.convert(GsonResponseBodyConverter.java:27)
W/System.err: at
retrofit2.ServiceMethod.toResponse(ServiceMethod.java:122)
W/System.err: at
retrofit2.OkHttpCall.parseResponse(OkHttpCall.java:217) W/System.err:
at retrofit2.OkHttpCall.execute(OkHttpCall.java:180) W/System.err:
at
retrofit2.adapter.rxjava2.CallExecuteObservable.subscribeActual(CallExecuteObservable.java:42) W/System.err: at
io.reactivex.Observable.subscribe(Observable.java:12084) W/System.err:
at
retrofit2.adapter.rxjava2.BodyObservable.subscribeActual(BodyObservable.java:34)
W/System.err: at
io.reactivex.Observable.subscribe(Observable.java:12084) W/System.err:
at
io.reactivex.internal.operators.observable.ObservableSingleSingle.subscribeActual(ObservableSingleSingle.java:35)
W/System.err: at io.reactivex.Single.subscribe(Single.java:3433)
W/System.err: at
io.reactivex.internal.operators.single.SingleDoOnSuccess.subscribeActual(SingleDoOnSuccess.java:35)
W/System.err: at io.reactivex.Single.subscribe(Single.java:3433)
W/System.err: at
io.reactivex.internal.operators.single.SingleSubscribeOn$SubscribeOnObserver.run(SingleSubscribeOn.java:89)
W/System.err: at
io.reactivex.Scheduler$DisposeTask.run(Scheduler.java:579)
W/System.err: at
io.reactivex.internal.schedulers.ScheduledRunnable.run(ScheduledRunnable.java:66)
W/System.err: at
io.reactivex.internal.schedulers.ScheduledRunnable.call(ScheduledRunnable.java:57)
W/System.err: at
java.util.concurrent.FutureTask.run(FutureTask.java:266) W/System.err:
at
java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:301)
W/System.err: at
java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
W/System.err: at
java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
W/System.err: at java.lang.Thread.run(Thread.java:764)
W/System.err: Caused by: java.lang.IllegalStateException: Expected
BEGIN_OBJECT but was STRING at line 1 column 1 path $ W/System.err:
at com.google.gson.stream.JsonReader.beginObject(JsonReader.java:385)
W/System.err: at
com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:213)
What could be causing this?
That error tells you that there is an error when GSON tries to parse the JSON returned by the web service you're calling. In particular, GSON is expecting a JSON object, but it receives a string (see the first line of the error message).
If the error happens only the first time you call it and not the second it means that the web service returns 2 different answers, and GSON can correctly parse the second one, but not the first one.
In order to find out what's wrong, you should provide more details, like your data model and the 2 responses returned by the web service.

java.net.SocketTimeoutException thrown only on some Android devices

I am trying to upload a JPEG image file from an Android device. I am using the square/okhttp library for creating a request. I am facing this issue on the Lenovo Yoga tablet, that when I am trying to upload the image, it gets the below exception. But when I run the same code on Samsung Galaxy Tab 10", it all works fine and the image gets uploaded successfully.
The camera captures the image and stores it at /storage/emulated/0/ from where the app will pick up the image and try to upload it. I have a service running in the background that does the upload.
final MediaType MEDIA_TYPE_IMAGE_JPEG = MediaType.parse("image/jpeg");
File file = new File(path);
Request request = new Request.Builder()
.url(baseUrl)
.addHeader("timestamp", map.get("timestamp"))
.addHeader("Content-Type", map.get("Content-Type"))
.post(RequestBody.create(MEDIA_TYPE_IMAGE_JPEG, file))
.build();
OkHttpClient client = new OkHttpClient();
Response response = client.newCall(request).execute();
Exception:
java.net.SocketTimeoutException: timeout
11-27 15:37:06.394 1770-2339/com.myapp.app.debug
W/System.err: at okhttp3.internal.http2.Http2Stream$StreamTimeout.newTimeoutException(Http2
Stream.java:593)
11-27 15:37:06.394 1770-2339/com.myapp.app.debug
W/System.err: at okhttp3.internal.http2.Http2Stream$StreamTimeout.exitAndThrowIfTimedOut(Http2Stream.java:601)
11-27 15:37:06.394 1770-2339/com.myapp.app.debug
W/System.err: at okhttp3.internal.http2.Http2Stream.takeResponseHeaders(Http2Stream.java:146)
11-27 15:37:06.394 1770-2339/com.myapp.app.debug
W/System.err: at okhttp3.internal.http2.Http2Codec.readResponseHeaders(Http2Codec.java:125)
11-27 15:37:06.394 1770-2339/com.myapp.app.debug
W/System.err: at okhttp3.internal.http.CallServerInterceptor.intercept(CallServerInterceptor.java:88)
11-27 15:37:06.394 1770-2339/com.myapp.app.debug
W/System.err: at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
11-27 15:37:06.394 1770-2339/commyapp.app.debug
W/System.err: at okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.java:45)
11-27 15:37:06.394 1770-2339/com.myapp.app.debug
W/System.err: at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
11-27 15:37:06.394 1770-2339/com.myapp.app.debug
W/System.err: at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
11-27 15:37:06.395 1770-2339/com.myapp.app.debug
W/System.err: at okhttp3.internal.cache.CacheInterceptor.intercept(CacheInterceptor.java:93)
11-27 15:37:06.395 1770-2339/com.myapp.app.debug
W/System.err: at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
11-27 15:37:06.395 1770-2339/com.myapp.app.debug
W/System.err: at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
11-27 15:37:06.395 1770-2339/com.myapp.app.debug
W/System.err: at okhttp3.internal.http.BridgeInterceptor.intercept(BridgeInterceptor.java:93)
11-27 15:37:06.395 1770-2339/com.myapp.app.debug
W/System.err: at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
11-27 15:37:06.395 1770-2339/com.myapp.app.debug
W/System.err: at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.java:125)
11-27 15:37:06.395 1770-2339/com.myapp.app.debug
W/System.err: at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
11-27 15:37:06.395 1770-2339/com.myapp.app.debug
W/System.err: at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
11-27 15:37:06.395 1770-2339/com.myapp.app.debug
W/System.err: at okhttp3.RealCall.getResponseWithInterceptorChain(RealCall.java:200)
11-27 15:37:06.395 1770-2339/com.myapp.app.debug
W/System.err: at okhttp3.RealCall.execute(RealCall.java:77)
You have to increase the read/write timeout on the server end. A temporary workaround for Android would be increasing the default timeout for OkHttp3 client:
OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(60, TimeUnit.SECONDS)
.writeTimeout(120, TimeUnit.SECONDS)
.readTimeout(60, TimeUnit.SECONDS)
.build();

Application crashes after I closed the socket waiting for reading in a thread

I have a service running in background which contains several threads reading and writing over TCP sockets. My application crashes after I close the sockets and stop the service. There is a SocketException thrown and catched by my code before the crash happens. I have no idea why the SocketExeption causes my application crashing when its been catched already. But this issue only occurs in one of my phones running on android 4.2. No crash happens on android 5 or andoird 6 so far.
Below is part of the code:
public void logout() throws IOException {
this.userName = "";
listenerService.p_status=false;
listenerService.p_Thread.interrupt();
listenerService.c_status=false;
listenerService.c_Thread.interrupt();
listenerService.closeSocket();
unbindService(mConnection);
listenerService.stopService();
}
Here is the debug information:
04-05 17:36:49.635 28101-28691/com.androidapp.ptt I/System.out: SOCKET CLOSED!
04-05 17:36:49.635 28101-28691/com.androidapp.ptt W/System.err: java.net.SocketException: Socket closed
04-05 17:36:49.635 28101-28691/com.androidapp.ptt W/System.err: at libcore.io.Posix.recvfromBytes(Native Method)
04-05 17:36:49.635 28101-28691/com.androidapp.ptt W/System.err: at libcore.io.Posix.recvfrom(Posix.java:142)
04-05 17:36:49.640 28101-28699/com.androidapp.ptt I/System.out: 404:kukukukukukuku
04-05 17:36:49.640 28101-28691/com.androidapp.ptt W/System.err: at libcore.io.BlockGuardOs.recvfrom(BlockGuardOs.java:164)
04-05 17:36:49.640 28101-28691/com.androidapp.ptt W/System.err: at libcore.io.IoBridge.recvfrom(IoBridge.java:499)
04-05 17:36:49.640 28101-28691/com.androidapp.ptt W/System.err: at java.net.PlainSocketImpl.read(PlainSocketImpl.java:488)
04-05 17:36:49.640 28101-28691/com.androidapp.ptt W/System.err: at java.net.PlainSocketImpl.access$000(PlainSocketImpl.java:46)
04-05 17:36:49.645 28101-28691/com.androidapp.ptt W/System.err: at java.net.PlainSocketImpl$PlainSocketInputStream.read(PlainSocketImpl.java:240)
04-05 17:36:49.645 28101-28691/com.androidapp.ptt W/System.err: at java.io.InputStream.read(InputStream.java:162)
04-05 17:36:49.645 28101-28691/com.androidapp.ptt W/System.err: at com.androidapp.ptt.ListenerService$2.run(ListenerService.java:439)
04-05 17:36:49.645 28101-28691/com.androidapp.ptt W/System.err: at java.lang.Thread.run(Thread.java:841)
04-05 17:36:49.685 28101-28101/com.androidapp.ptt I/System.out: Service done!
Any help is much appreciated!
The thread that was blocked in read() threw an IOException. This is exactly the behaviour that should be expected. You closed the socket: it's closed. You can't continue reading.
If you want a nice way to terminate that thread, call shutdownInput() on the socket instead of closing it. That will cause the reading thread to unblock, perceive end of stream, and, if correctty written, stop reading and close the socket.

Volley SSL error when uploading a file larger than 1mb (I/O error during system call, Connection reset by peer)

I tried adding a retry policy (https://stackoverflow.com/a/22169775/2098493)
but the error persist when file being uploaded is larger than 1mb.
W/System.err: com.android.volley.NoConnectionError: javax.net.ssl.SSLException: Write error: ssl=0xafc3fe00: I/O error during system call, Connection reset by peer
W/System.err: at com.android.volley.toolbox.BasicNetwork.performRequest(BasicNetwork.java:151)
W/System.err: at com.android.volley.NetworkDispatcher.run(NetworkDispatcher.java:112)
W/System.err: Caused by: javax.net.ssl.SSLException: Write error: ssl=0xafc3fe00: I/O error during system call, Connection reset by peer
W/System.err: at com.android.org.conscrypt.NativeCrypto.SSL_write(Native Method)
W/System.err: at com.android.org.conscrypt.OpenSSLSocketImpl$SSLOutputStream.write(OpenSSLSocketImpl.java:765)
W/System.err: at com.android.okio.Okio$1.write(Okio.java:70)
W/System.err: at com.android.okio.RealBufferedSink.emitCompleteSegments(RealBufferedSink.java:116)
W/System.err: at com.android.okio.RealBufferedSink.write(RealBufferedSink.java:44)
W/System.err: at com.android.okhttp.internal.http.RetryableSink.writeToSocket(RetryableSink.java:77)
W/System.err: at com.android.okhttp.internal.http.HttpConnection.writeRequestBody(HttpConnection.java:240)
W/System.err: at com.android.okhttp.internal.http.HttpTransport.writeRequestBody(HttpTransport.java:77)
W/System.err: at com.android.okhttp.internal.http.HttpEngine.readResponse(HttpEngine.java:622)
W/System.err: at com.android.okhttp.internal.http.HttpURLConnectionImpl.execute(HttpURLConnectionImpl.java:388)
W/System.err: at com.android.okhttp.internal.http.HttpURLConnectionImpl.getResponse(HttpURLConnectionImpl.java:332)
W/System.err: at com.android.okhttp.internal.http.HttpURLConnectionImpl.getResponseCode(HttpURLConnectionImpl.java:500)
W/System.err: at com.android.okhttp.internal.http.DelegatingHttpsURLConnection.getResponseCode(DelegatingHttpsURLConnection.java:105)
W/System.err: at com.android.okhttp.internal.http.HttpsURLConnectionImpl.getResponseCode(HttpsURLConnectionImpl.java:25)
W/System.err: at com.android.volley.toolbox.HurlStack.performRequest(HurlStack.java:110)
W/System.err: at com.android.volley.toolbox.BasicNetwork.performRequest(BasicNetwork.java:96)
Thanks for your advice.
The solution was a combination of adding Volley Retry Policy and making sure back-end and server (nodejs/nginx) max uploaded file size are both set correctly.
//where RETRY_DEFAULT_TIMEOUT_MS = 5000, RETRY_DEFAULT_MAX_RETRIES = 3, RETRY_DEFAULT_BACKOFF_MULT = 2.0f
request.setRetryPolicy(new DefaultRetryPolicy(RETRY_DEFAULT_TIMEOUT_MS,
RETRY_DEFAULT_MAX_RETRIES,
RETRY_DEFAULT_BACKOFF_MULT));

Android/Java - JSONException, java.util.HashMap cannot be converted to JSONObject API < 21

I am running into a very strange problem on devices running API < 21.
I have the following JSONObject
{
"saturday":"{notes=Walk-Ins Only, open=2016-01-16T08:00:00-05:00, close=2016-01-16T15:00:00-05:00}",
"wednesday":"{notes=Walk-Ins Only, open=2016-01-20T09:00:00-05:00, close=2016-01-20T18:45:00-05:00}",
"tuesday":"{notes=Walk-Ins Only, open=2016-01-19T09:00:00-05:00, close=2016-01-19T18:45:00-05:00}",
"friday":"{notes=Walk-Ins Only, open=2016-01-22T09:00:00-05:00, close=2016-01-22T18:45:00-05:00}",
"thursday":"{notes=Appointments & Walk-Ins, open=2016-01-21T09:00:00-05:00, close=2016-01-21T18:45:00-05:00}",
"monday":"{notes=null, open=null, close=null}",
"sunday":"{notes=null, open=null, close=null}"
}
When attempting to get the JSONObject for a given day, on devices running API >= 21 I am about to use the following example code
jsonObject.getJSONObject("tuesday")
which successfully returns a JSONObject, however, on devices running API < 21 the same exact code is throwing
Value {notes=Walk-Ins Only, open=2016-01-19T09:00:00-05:00, close=2016-01-19T18:45:00-05:00} at tuesday of type java.util.HashMap cannot be converted to JSONObject
Why is this happening?
Edit: My mapping code isn't anything special -
JSONObject loungeHours = new JSONObject(ParseCloud.<Map>callFunction(STORE_HOURS_ENDPOINT, new HashMap<String, Object>()));
When analyzed with the debugger, loungeHours is successfully populated with the following
{
"saturday":"{notes=Walk-Ins Only, open=2016-01-16T08:00:00-05:00, close=2016-01-16T15:00:00-05:00}",
"wednesday":"{notes=Walk-Ins Only, open=2016-01-20T09:00:00-05:00, close=2016-01-20T18:45:00-05:00}",
"tuesday":"{notes=Walk-Ins Only, open=2016-01-19T09:00:00-05:00, close=2016-01-19T18:45:00-05:00}",
"friday":"{notes=Walk-Ins Only, open=2016-01-22T09:00:00-05:00, close=2016-01-22T18:45:00-05:00}",
"thursday":"{notes=Appointments & Walk-Ins, open=2016-01-21T09:00:00-05:00, close=2016-01-21T18:45:00-05:00}",
"monday":"{notes=null, open=null, close=null}",
"sunday":"{notes=null, open=null, close=null}"
}
Full stacktrace
W/System.err: org.json.JSONException: Value {notes=Walk-Ins Only, open=2016-01-19T09:00:00-05:00, close=2016-01-19T18:45:00-05:00} at tuesday of type java.util.HashMap cannot be converted to JSONObject
W/System.err: at org.json.JSON.typeMismatch(JSON.java:100)
W/System.err: at org.json.JSONObject.getJSONObject(JSONObject.java:573)
W/System.err: at com.hitchtech.adamsbarberlounge.parse.ParseDelegate.getOpenHours(ParseDelegate.java:127)
W/System.err: at com.hitchtech.adamsbarberlounge.application.main.LoungeDetailFragment$LoungeInfoCardController.populateHours(LoungeDetailFragment.java:247)
W/System.err: at com.hitchtech.adamsbarberlounge.application.main.LoungeDetailFragment$LoungeInfoCardController.<init>(LoungeDetailFragment.java:105)
W/System.err: at com.hitchtech.adamsbarberlounge.application.main.LoungeDetailFragment.setupLoungeInfoCardController(LoungeDetailFragment.java:73)
W/System.err: at com.hitchtech.adamsbarberlounge.application.main.LoungeDetailFragment.onViewCreated(LoungeDetailFragment.java:64)
W/System.err: at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1086)
W/System.err: at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1248)
W/System.err: at android.support.v4.app.BackStackRecord.run(BackStackRecord.java:738)
W/System.err: at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1613)
W/System.err: at android.support.v4.app.FragmentController.execPendingActions(FragmentController.java:330)
W/System.err: at android.support.v4.app.FragmentActivity.onResume(FragmentActivity.java:441)
W/System.err: at android.app.Instrumentation.callActivityOnResume(Instrumentation.java:1185)
W/System.err: at android.app.Activity.performResume(Activity.java:5182)
W/System.err: at android.app.ActivityThread.performResumeActivity(ActivityThread.java:2732)
W/System.err: at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:2771)
W/System.err: at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2235)
W/System.err: at android.app.ActivityThread.access$600(ActivityThread.java:141)
W/System.err: at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1234)
W/System.err: at android.os.Handler.dispatchMessage(Handler.java:99)
W/System.err: at android.os.Looper.loop(Looper.java:137)
W/System.err: at android.app.ActivityThread.main(ActivityThread.java:5041)
W/System.err: at java.lang.reflect.Method.invokeNative(Native Method)
W/System.err: at java.lang.reflect.Method.invoke(Method.java:511)
W/System.err: at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:793)
W/System.err: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:560)
W/System.err: at dalvik.system.NativeStart.main(Native Method)

Categories

Resources