RXJava HTTP 500 Internal Server











up vote
0
down vote

favorite












I have converted RAVI TAMADA's RxJava tutorial from Java into Kotlin Android RxJava Networking with Retrofit, Gson – Notes App. On testing of the application just on the first network call I receive a HTTP 500 Internal Error (com.jakewharton.retrofit2.adapter.rxjava2.HttpException: HTTP 500 Internal Server Error). This is for the first network call registerUsers As far as conversion is concerned I have done everything by the book.



I have included the code base for the following classes



MainActivity



class MainActivity : AppCompatActivity()
{

lateinit var apiService: ApiService
var disposable = CompositeDisposable()
lateinit var mAdapter: NotesAdapter
var noteList = ArrayList<Note>()

companion object
{
val TAG = MainActivity::class.java.simpleName;

}

@BindView(R.id.coordinator_layout) var coordinatorLayout: CoordinatorLayout? = null

@BindView(R.id.recycler_view) var recyclerView: RecyclerView? = null

@BindView(R.id.txt_empty_notes_view) var noNotesView: TextView? = null

override fun onCreate(savedInstanceState: Bundle?)
{
super.onCreate(savedInstanceState)

getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);

val toolbar = findViewById<Toolbar>(R.id.toolbar)
toolbar.setTitle(getString(R.string.activity_title_home))
setSupportActionBar(toolbar)

fab.setOnClickListener { view ->

showNoteDialog(false, null, -1);

}

// white background notification bar
whiteNotificationBar(fab);

apiService = ApiClient.getClient(getApplicationContext())?.create(ApiService::class.java)!!

mAdapter = NotesAdapter(this, noteList)
var mLayoutManager = LinearLayoutManager(getApplicationContext());
recyclerView?.setLayoutManager(mLayoutManager);
recyclerView?.setItemAnimator(DefaultItemAnimator());
recyclerView?.addItemDecoration(MyDividerItemDecoration(this, LinearLayoutManager.VERTICAL, 16));
recyclerView?.setAdapter(mAdapter);

/**
* On long press on RecyclerView item, open alert dialog
* with options to choose
* Edit and Delete
* */
recyclerView?.addOnItemTouchListener(RecyclerTouchListener(this, recyclerView!!, object : RecyclerTouchListener.ClickListener
{

override fun onClick(view: View, position: Int)
{
}

override fun onLongClick(view: View, position: Int)
{
showActionsDialog(position);
}
}))

/**
* Check for stored Api Key in shared preferences
* If not present, make api call to register the user
* This will be executed when app is installed for the first time
* or data is cleared from settings
* */

var test = PrefUtils.getApiKey(this)
if (TextUtils.isEmpty(PrefUtils?.getApiKey(this)))
{
registerUser();
} else
{
// user is already registered, fetch all notes
fetchAllNotes();
}
}

override fun onCreateOptionsMenu(menu: Menu): Boolean
{
// Inflate the menu; this adds items to the action bar if it is present.
menuInflater.inflate(R.menu.menu_main, menu)
return true
}

override fun onOptionsItemSelected(item: MenuItem): Boolean
{
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
return when (item.itemId)
{
R.id.action_settings -> true
else -> super.onOptionsItemSelected(item)
}
}


/**
* Registering new user
* sending unique id as device identification
* https://developer.android.com/training/articles/user-data-ids.html
*/
private fun registerUser()
{
// unique id to identify the device
val uniqueId = UUID.randomUUID().toString()

disposable
.add(apiService.register(uniqueId)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(
object : DisposableSingleObserver<User>()
{
override fun onSuccess(user: User)
{
// Storing user API Key in preferences
user.apiKey?.let { PrefUtils.storeApiKey(applicationContext, it) }

Toast.makeText(applicationContext,
"Device is registered successfully! ApiKey: " + PrefUtils.getApiKey(applicationContext),
Toast.LENGTH_LONG).show()
}

override fun onError(e: Throwable)
{
Log.e(TAG, "onError: " + e.message)
showError(e)
}
}))
}

/**
* Fetching all notes from api
* The received items will be in random order
* map() operator is used to sort the items in descending order by Id
*/
fun fetchAllNotes()
{
disposable.add(apiService.fetchAllNotes().subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).map(
object : io.reactivex.functions.Function<List<Note>, List<Note>>
{
override fun apply(notes: List<Note>): List<Note>
{
Collections.sort(notes, object : Comparator<Note>
{
override fun compare(n1: Note?, n2: Note?): Int
{
return n2!!.id - n1!!.id;

}
})
return notes
}
}).subscribeWith(object : DisposableSingleObserver<List<Note>>()
{
override fun onSuccess(notes: List<Note>)
{
noteList.clear();
noteList.addAll(notes);
mAdapter.notifyDataSetChanged();

toggleEmptyNotes();
}

override fun onError(e: Throwable)
{
Log.e(TAG, "onError: " + e.message);
showError(e);


}
}))
}


/**
* Creating new note
*/
private fun createNote(note: String)
{
disposable.add(apiService.createNote(note)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(
object : DisposableSingleObserver<Note>()
{

override fun onSuccess(note: Note)
{
if (!TextUtils.isEmpty(note.error))
{
Toast.makeText(applicationContext, note.error, Toast.LENGTH_LONG).show()
return
}

Log.d(TAG, "new note created: " + note.id + ", " + note.note + ", " + note.timestamp)

// Add new item and notify adapter
noteList.add(0, note)
mAdapter.notifyItemInserted(0)

toggleEmptyNotes()
}

override fun onError(e: Throwable)
{
Log.e(TAG, "onError: " + e.message)
showError(e)
}
}))
}

/**
* Updating a note
*/
private fun updateNote(noteId: Int, note: String, position: Int)
{
disposable
.add(apiService.updateNote(noteId,
note)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(object :
DisposableCompletableObserver()
{
override fun onComplete()
{
Log.d(TAG, "Note updated!")

val n = noteList.get(position)
n.note = (note)

// Update item and notify adapter
noteList.set(position, n)
mAdapter.notifyItemChanged(position)
}

override fun onError(e: Throwable)
{
Log.e(TAG, "onError: " + e.message)
showError(e)
}
}))
}

/**
* Deleting a note
*/
private fun deleteNote(noteId: Int, position: Int)
{
Log.e(TAG, "deleteNote: $noteId, $position")
disposable
.add(apiService.deleteNote(noteId)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(
object : DisposableCompletableObserver()
{
override fun onComplete()
{
Log.d(TAG, "Note deleted! $noteId")

// Remove and notify adapter about item deletion
noteList.removeAt(position)
mAdapter.notifyItemRemoved(position)

Toast.makeText(this@MainActivity, "Note deleted!", Toast.LENGTH_SHORT).show()

toggleEmptyNotes()
}

override fun onError(e: Throwable)
{
Log.e(TAG, "onError: " + e.message)
showError(e)
}
}))
}

/**
* Shows alert dialog with EditText options to enter / edit
* a note.
* when shouldUpdate=true, it automatically displays old note and changes the
* button text to UPDATE
*/
private fun showNoteDialog(shouldUpdate: Boolean, note: Note?, position: Int)
{
val layoutInflaterAndroid = LayoutInflater.from(applicationContext)
val view = layoutInflaterAndroid.inflate(R.layout.note_dialog, null)

val alertDialogBuilderUserInput = AlertDialog.Builder(this@MainActivity)
alertDialogBuilderUserInput.setView(view)

val inputNote = view.findViewById<EditText>(R.id.note)
val dialogTitle = view.findViewById<TextView>(R.id.dialog_title)
dialogTitle.setText(if (!shouldUpdate) getString(R.string.lbl_new_note_title) else getString(R.string.lbl_edit_note_title))

if (shouldUpdate && note != null)
{
inputNote.setText(note.note)
}
alertDialogBuilderUserInput.setCancelable(false).setPositiveButton(if (shouldUpdate) "update" else "save",
DialogInterface.OnClickListener { dialogBox, id -> })
.setNegativeButton("cancel", DialogInterface.OnClickListener { dialogBox, id -> dialogBox.cancel() })

val alertDialog = alertDialogBuilderUserInput.create()
alertDialog.show()

alertDialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(View.OnClickListener {
// Show toast message when no text is entered
if (TextUtils.isEmpty(inputNote.text.toString()))
{
Toast.makeText(this@MainActivity, "Enter note!", Toast.LENGTH_SHORT).show()
return@OnClickListener
} else
{
alertDialog.dismiss()
}

// check if user updating note
if (shouldUpdate && note != null)
{
// update note by it's id
updateNote(note.id, inputNote.text.toString(), position)
} else
{
// create new note
createNote(inputNote.text.toString())
}
})
}

/**
* Opens dialog with Edit - Delete options
* Edit - 0
* Delete - 0
*/
private fun showActionsDialog(position: Int)
{
val colors = arrayOf<CharSequence>("Edit", "Delete")

val builder = AlertDialog.Builder(this)
builder.setTitle("Choose option")
builder.setItems(colors) { dialog, which ->
if (which == 0)
{
showNoteDialog(true, noteList.get(position), position)
} else
{
deleteNote(noteList.get(position).id, position)
}
}
builder.show()
}

private fun toggleEmptyNotes()
{
if (noteList.size > 0)
{
noNotesView?.setVisibility(View.GONE)
} else
{
noNotesView?.setVisibility(View.VISIBLE)
}
}


/**
* Showing a Snackbar with error message
* The error body will be in json format
* {"error": "Error message!"}
*/
fun showError(e: Throwable)
{
var message = ""
try
{
if (e is IOException)
{
message = "No internet connection!"
}
else (e is HttpException)
run {
var error = e as HttpException
var errorBody = error.response().errorBody().toString()
var jObj = JSONObject(errorBody)
message = jObj.getString("error")



}

}
catch (e1: IOException)
{
e1.printStackTrace()
}
catch (e1: JSONException)
{
e1.printStackTrace()
}
catch (e1: Exception)
{
e1.printStackTrace()
}

if (TextUtils.isEmpty(message))
{
message = "Unknown error occurred! Check LogCat."
}

val snackbar = coordinatorLayout?.let { Snackbar.make(it, message, Snackbar.LENGTH_LONG) }

val sbView = snackbar?.getView()
val textView = sbView?.findViewById<TextView>(android.support.design.R.id.snackbar_text)
textView?.setTextColor(Color.YELLOW)
snackbar?.show()
}

fun whiteNotificationBar(view: View)
{
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
{
var flags = view.getSystemUiVisibility()
flags = View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
view.setSystemUiVisibility(flags)
getWindow().setStatusBarColor(Color.WHITE)
}
}

override fun onDestroy()
{
super.onDestroy()
disposable.dispose()
}
}


ApiService Interface



interface  ApiService
{
// Register new user
@FormUrlEncoded
@POST("notes/user/register")
fun register(@Field("device_id") deviceId: String): Single<User>
// Single<User> register(@Field("device_id") String deviceId)

// Create note
@FormUrlEncoded
@POST("notes/new")
fun createNote(@Field("note") note: String): Single<Note>

// Fetch all notes
@GET("notes/all") fun fetchAllNotes(): Single<List<Note>>

// Update single note
@FormUrlEncoded
@PUT("notes/{id}")
fun updateNote(@Path("id") noteId: Int, @Field("note") note: String): Completable

// Delete note
@DELETE("notes/{id}")
fun deleteNote(@Path("id") noteId: Int): Completable
}


ApiClient Class



class ApiClient
{
companion object
{
var retrofit: Retrofit? = null
var REQUEST_TIMEOUT = 60
var okHttpClient: OkHttpClient? = null

fun getClient(context: Context): Retrofit?
{
if (okHttpClient == null)
initOkHttp(context)

if (retrofit == null)
{
retrofit = Retrofit.Builder().baseUrl(BASE_URL).client(okHttpClient)
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create()).build()
}
return retrofit
}

fun initOkHttp(context: Context)
{
val httpClient = OkHttpClient().newBuilder().connectTimeout(REQUEST_TIMEOUT.toLong(), TimeUnit.SECONDS)
.readTimeout(REQUEST_TIMEOUT.toLong(), TimeUnit.SECONDS)
.writeTimeout(REQUEST_TIMEOUT.toLong(), TimeUnit.SECONDS)

val interceptor = HttpLoggingInterceptor()
interceptor.level = HttpLoggingInterceptor.Level.BODY

httpClient.addInterceptor(interceptor)

httpClient.addInterceptor(object : Interceptor
{
override fun intercept(chain: Interceptor.Chain): Response
{
var original = chain.request()
var requestBuilder = original.newBuilder()

.addHeader("Accept", "application/json").addHeader("Content-Type", "application/json");

// Adding Authorization token (API Key)
// Requests will be denied without API key
if (!TextUtils.isEmpty(PrefUtils.getApiKey(context)))
{
requestBuilder.addHeader("Authorization", PrefUtils.getApiKey(context));
}

var request = requestBuilder.build();

return chain.proceed(request)
}


})

okHttpClient = httpClient.build()
}
}
}


PrefUtils Class



 class PrefUtils
{
/**
* Storing API Key in shared preferences to
* add it in header part of every retrofit request
*/


companion object
{

fun getSharedPreferences(context: Context): SharedPreferences
{
return context.getSharedPreferences("APP_PREF", Context.MODE_PRIVATE)
}

fun storeApiKey(context: Context, apiKey: String)
{
val editor = getSharedPreferences(context).edit()
editor.putString("API_KEY", apiKey)
editor.commit()
}

fun getApiKey(context: Context): String?
{
return getSharedPreferences(context).getString("API_KEY", null)
}
}
}









share|improve this question




















  • 1




    all of the code is irrelevant. 500 means there is an error on the server. Check the server logs. We cannot help you
    – Tim Castelijns
    Nov 20 at 12:49















up vote
0
down vote

favorite












I have converted RAVI TAMADA's RxJava tutorial from Java into Kotlin Android RxJava Networking with Retrofit, Gson – Notes App. On testing of the application just on the first network call I receive a HTTP 500 Internal Error (com.jakewharton.retrofit2.adapter.rxjava2.HttpException: HTTP 500 Internal Server Error). This is for the first network call registerUsers As far as conversion is concerned I have done everything by the book.



I have included the code base for the following classes



MainActivity



class MainActivity : AppCompatActivity()
{

lateinit var apiService: ApiService
var disposable = CompositeDisposable()
lateinit var mAdapter: NotesAdapter
var noteList = ArrayList<Note>()

companion object
{
val TAG = MainActivity::class.java.simpleName;

}

@BindView(R.id.coordinator_layout) var coordinatorLayout: CoordinatorLayout? = null

@BindView(R.id.recycler_view) var recyclerView: RecyclerView? = null

@BindView(R.id.txt_empty_notes_view) var noNotesView: TextView? = null

override fun onCreate(savedInstanceState: Bundle?)
{
super.onCreate(savedInstanceState)

getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);

val toolbar = findViewById<Toolbar>(R.id.toolbar)
toolbar.setTitle(getString(R.string.activity_title_home))
setSupportActionBar(toolbar)

fab.setOnClickListener { view ->

showNoteDialog(false, null, -1);

}

// white background notification bar
whiteNotificationBar(fab);

apiService = ApiClient.getClient(getApplicationContext())?.create(ApiService::class.java)!!

mAdapter = NotesAdapter(this, noteList)
var mLayoutManager = LinearLayoutManager(getApplicationContext());
recyclerView?.setLayoutManager(mLayoutManager);
recyclerView?.setItemAnimator(DefaultItemAnimator());
recyclerView?.addItemDecoration(MyDividerItemDecoration(this, LinearLayoutManager.VERTICAL, 16));
recyclerView?.setAdapter(mAdapter);

/**
* On long press on RecyclerView item, open alert dialog
* with options to choose
* Edit and Delete
* */
recyclerView?.addOnItemTouchListener(RecyclerTouchListener(this, recyclerView!!, object : RecyclerTouchListener.ClickListener
{

override fun onClick(view: View, position: Int)
{
}

override fun onLongClick(view: View, position: Int)
{
showActionsDialog(position);
}
}))

/**
* Check for stored Api Key in shared preferences
* If not present, make api call to register the user
* This will be executed when app is installed for the first time
* or data is cleared from settings
* */

var test = PrefUtils.getApiKey(this)
if (TextUtils.isEmpty(PrefUtils?.getApiKey(this)))
{
registerUser();
} else
{
// user is already registered, fetch all notes
fetchAllNotes();
}
}

override fun onCreateOptionsMenu(menu: Menu): Boolean
{
// Inflate the menu; this adds items to the action bar if it is present.
menuInflater.inflate(R.menu.menu_main, menu)
return true
}

override fun onOptionsItemSelected(item: MenuItem): Boolean
{
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
return when (item.itemId)
{
R.id.action_settings -> true
else -> super.onOptionsItemSelected(item)
}
}


/**
* Registering new user
* sending unique id as device identification
* https://developer.android.com/training/articles/user-data-ids.html
*/
private fun registerUser()
{
// unique id to identify the device
val uniqueId = UUID.randomUUID().toString()

disposable
.add(apiService.register(uniqueId)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(
object : DisposableSingleObserver<User>()
{
override fun onSuccess(user: User)
{
// Storing user API Key in preferences
user.apiKey?.let { PrefUtils.storeApiKey(applicationContext, it) }

Toast.makeText(applicationContext,
"Device is registered successfully! ApiKey: " + PrefUtils.getApiKey(applicationContext),
Toast.LENGTH_LONG).show()
}

override fun onError(e: Throwable)
{
Log.e(TAG, "onError: " + e.message)
showError(e)
}
}))
}

/**
* Fetching all notes from api
* The received items will be in random order
* map() operator is used to sort the items in descending order by Id
*/
fun fetchAllNotes()
{
disposable.add(apiService.fetchAllNotes().subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).map(
object : io.reactivex.functions.Function<List<Note>, List<Note>>
{
override fun apply(notes: List<Note>): List<Note>
{
Collections.sort(notes, object : Comparator<Note>
{
override fun compare(n1: Note?, n2: Note?): Int
{
return n2!!.id - n1!!.id;

}
})
return notes
}
}).subscribeWith(object : DisposableSingleObserver<List<Note>>()
{
override fun onSuccess(notes: List<Note>)
{
noteList.clear();
noteList.addAll(notes);
mAdapter.notifyDataSetChanged();

toggleEmptyNotes();
}

override fun onError(e: Throwable)
{
Log.e(TAG, "onError: " + e.message);
showError(e);


}
}))
}


/**
* Creating new note
*/
private fun createNote(note: String)
{
disposable.add(apiService.createNote(note)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(
object : DisposableSingleObserver<Note>()
{

override fun onSuccess(note: Note)
{
if (!TextUtils.isEmpty(note.error))
{
Toast.makeText(applicationContext, note.error, Toast.LENGTH_LONG).show()
return
}

Log.d(TAG, "new note created: " + note.id + ", " + note.note + ", " + note.timestamp)

// Add new item and notify adapter
noteList.add(0, note)
mAdapter.notifyItemInserted(0)

toggleEmptyNotes()
}

override fun onError(e: Throwable)
{
Log.e(TAG, "onError: " + e.message)
showError(e)
}
}))
}

/**
* Updating a note
*/
private fun updateNote(noteId: Int, note: String, position: Int)
{
disposable
.add(apiService.updateNote(noteId,
note)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(object :
DisposableCompletableObserver()
{
override fun onComplete()
{
Log.d(TAG, "Note updated!")

val n = noteList.get(position)
n.note = (note)

// Update item and notify adapter
noteList.set(position, n)
mAdapter.notifyItemChanged(position)
}

override fun onError(e: Throwable)
{
Log.e(TAG, "onError: " + e.message)
showError(e)
}
}))
}

/**
* Deleting a note
*/
private fun deleteNote(noteId: Int, position: Int)
{
Log.e(TAG, "deleteNote: $noteId, $position")
disposable
.add(apiService.deleteNote(noteId)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(
object : DisposableCompletableObserver()
{
override fun onComplete()
{
Log.d(TAG, "Note deleted! $noteId")

// Remove and notify adapter about item deletion
noteList.removeAt(position)
mAdapter.notifyItemRemoved(position)

Toast.makeText(this@MainActivity, "Note deleted!", Toast.LENGTH_SHORT).show()

toggleEmptyNotes()
}

override fun onError(e: Throwable)
{
Log.e(TAG, "onError: " + e.message)
showError(e)
}
}))
}

/**
* Shows alert dialog with EditText options to enter / edit
* a note.
* when shouldUpdate=true, it automatically displays old note and changes the
* button text to UPDATE
*/
private fun showNoteDialog(shouldUpdate: Boolean, note: Note?, position: Int)
{
val layoutInflaterAndroid = LayoutInflater.from(applicationContext)
val view = layoutInflaterAndroid.inflate(R.layout.note_dialog, null)

val alertDialogBuilderUserInput = AlertDialog.Builder(this@MainActivity)
alertDialogBuilderUserInput.setView(view)

val inputNote = view.findViewById<EditText>(R.id.note)
val dialogTitle = view.findViewById<TextView>(R.id.dialog_title)
dialogTitle.setText(if (!shouldUpdate) getString(R.string.lbl_new_note_title) else getString(R.string.lbl_edit_note_title))

if (shouldUpdate && note != null)
{
inputNote.setText(note.note)
}
alertDialogBuilderUserInput.setCancelable(false).setPositiveButton(if (shouldUpdate) "update" else "save",
DialogInterface.OnClickListener { dialogBox, id -> })
.setNegativeButton("cancel", DialogInterface.OnClickListener { dialogBox, id -> dialogBox.cancel() })

val alertDialog = alertDialogBuilderUserInput.create()
alertDialog.show()

alertDialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(View.OnClickListener {
// Show toast message when no text is entered
if (TextUtils.isEmpty(inputNote.text.toString()))
{
Toast.makeText(this@MainActivity, "Enter note!", Toast.LENGTH_SHORT).show()
return@OnClickListener
} else
{
alertDialog.dismiss()
}

// check if user updating note
if (shouldUpdate && note != null)
{
// update note by it's id
updateNote(note.id, inputNote.text.toString(), position)
} else
{
// create new note
createNote(inputNote.text.toString())
}
})
}

/**
* Opens dialog with Edit - Delete options
* Edit - 0
* Delete - 0
*/
private fun showActionsDialog(position: Int)
{
val colors = arrayOf<CharSequence>("Edit", "Delete")

val builder = AlertDialog.Builder(this)
builder.setTitle("Choose option")
builder.setItems(colors) { dialog, which ->
if (which == 0)
{
showNoteDialog(true, noteList.get(position), position)
} else
{
deleteNote(noteList.get(position).id, position)
}
}
builder.show()
}

private fun toggleEmptyNotes()
{
if (noteList.size > 0)
{
noNotesView?.setVisibility(View.GONE)
} else
{
noNotesView?.setVisibility(View.VISIBLE)
}
}


/**
* Showing a Snackbar with error message
* The error body will be in json format
* {"error": "Error message!"}
*/
fun showError(e: Throwable)
{
var message = ""
try
{
if (e is IOException)
{
message = "No internet connection!"
}
else (e is HttpException)
run {
var error = e as HttpException
var errorBody = error.response().errorBody().toString()
var jObj = JSONObject(errorBody)
message = jObj.getString("error")



}

}
catch (e1: IOException)
{
e1.printStackTrace()
}
catch (e1: JSONException)
{
e1.printStackTrace()
}
catch (e1: Exception)
{
e1.printStackTrace()
}

if (TextUtils.isEmpty(message))
{
message = "Unknown error occurred! Check LogCat."
}

val snackbar = coordinatorLayout?.let { Snackbar.make(it, message, Snackbar.LENGTH_LONG) }

val sbView = snackbar?.getView()
val textView = sbView?.findViewById<TextView>(android.support.design.R.id.snackbar_text)
textView?.setTextColor(Color.YELLOW)
snackbar?.show()
}

fun whiteNotificationBar(view: View)
{
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
{
var flags = view.getSystemUiVisibility()
flags = View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
view.setSystemUiVisibility(flags)
getWindow().setStatusBarColor(Color.WHITE)
}
}

override fun onDestroy()
{
super.onDestroy()
disposable.dispose()
}
}


ApiService Interface



interface  ApiService
{
// Register new user
@FormUrlEncoded
@POST("notes/user/register")
fun register(@Field("device_id") deviceId: String): Single<User>
// Single<User> register(@Field("device_id") String deviceId)

// Create note
@FormUrlEncoded
@POST("notes/new")
fun createNote(@Field("note") note: String): Single<Note>

// Fetch all notes
@GET("notes/all") fun fetchAllNotes(): Single<List<Note>>

// Update single note
@FormUrlEncoded
@PUT("notes/{id}")
fun updateNote(@Path("id") noteId: Int, @Field("note") note: String): Completable

// Delete note
@DELETE("notes/{id}")
fun deleteNote(@Path("id") noteId: Int): Completable
}


ApiClient Class



class ApiClient
{
companion object
{
var retrofit: Retrofit? = null
var REQUEST_TIMEOUT = 60
var okHttpClient: OkHttpClient? = null

fun getClient(context: Context): Retrofit?
{
if (okHttpClient == null)
initOkHttp(context)

if (retrofit == null)
{
retrofit = Retrofit.Builder().baseUrl(BASE_URL).client(okHttpClient)
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create()).build()
}
return retrofit
}

fun initOkHttp(context: Context)
{
val httpClient = OkHttpClient().newBuilder().connectTimeout(REQUEST_TIMEOUT.toLong(), TimeUnit.SECONDS)
.readTimeout(REQUEST_TIMEOUT.toLong(), TimeUnit.SECONDS)
.writeTimeout(REQUEST_TIMEOUT.toLong(), TimeUnit.SECONDS)

val interceptor = HttpLoggingInterceptor()
interceptor.level = HttpLoggingInterceptor.Level.BODY

httpClient.addInterceptor(interceptor)

httpClient.addInterceptor(object : Interceptor
{
override fun intercept(chain: Interceptor.Chain): Response
{
var original = chain.request()
var requestBuilder = original.newBuilder()

.addHeader("Accept", "application/json").addHeader("Content-Type", "application/json");

// Adding Authorization token (API Key)
// Requests will be denied without API key
if (!TextUtils.isEmpty(PrefUtils.getApiKey(context)))
{
requestBuilder.addHeader("Authorization", PrefUtils.getApiKey(context));
}

var request = requestBuilder.build();

return chain.proceed(request)
}


})

okHttpClient = httpClient.build()
}
}
}


PrefUtils Class



 class PrefUtils
{
/**
* Storing API Key in shared preferences to
* add it in header part of every retrofit request
*/


companion object
{

fun getSharedPreferences(context: Context): SharedPreferences
{
return context.getSharedPreferences("APP_PREF", Context.MODE_PRIVATE)
}

fun storeApiKey(context: Context, apiKey: String)
{
val editor = getSharedPreferences(context).edit()
editor.putString("API_KEY", apiKey)
editor.commit()
}

fun getApiKey(context: Context): String?
{
return getSharedPreferences(context).getString("API_KEY", null)
}
}
}









share|improve this question




















  • 1




    all of the code is irrelevant. 500 means there is an error on the server. Check the server logs. We cannot help you
    – Tim Castelijns
    Nov 20 at 12:49













up vote
0
down vote

favorite









up vote
0
down vote

favorite











I have converted RAVI TAMADA's RxJava tutorial from Java into Kotlin Android RxJava Networking with Retrofit, Gson – Notes App. On testing of the application just on the first network call I receive a HTTP 500 Internal Error (com.jakewharton.retrofit2.adapter.rxjava2.HttpException: HTTP 500 Internal Server Error). This is for the first network call registerUsers As far as conversion is concerned I have done everything by the book.



I have included the code base for the following classes



MainActivity



class MainActivity : AppCompatActivity()
{

lateinit var apiService: ApiService
var disposable = CompositeDisposable()
lateinit var mAdapter: NotesAdapter
var noteList = ArrayList<Note>()

companion object
{
val TAG = MainActivity::class.java.simpleName;

}

@BindView(R.id.coordinator_layout) var coordinatorLayout: CoordinatorLayout? = null

@BindView(R.id.recycler_view) var recyclerView: RecyclerView? = null

@BindView(R.id.txt_empty_notes_view) var noNotesView: TextView? = null

override fun onCreate(savedInstanceState: Bundle?)
{
super.onCreate(savedInstanceState)

getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);

val toolbar = findViewById<Toolbar>(R.id.toolbar)
toolbar.setTitle(getString(R.string.activity_title_home))
setSupportActionBar(toolbar)

fab.setOnClickListener { view ->

showNoteDialog(false, null, -1);

}

// white background notification bar
whiteNotificationBar(fab);

apiService = ApiClient.getClient(getApplicationContext())?.create(ApiService::class.java)!!

mAdapter = NotesAdapter(this, noteList)
var mLayoutManager = LinearLayoutManager(getApplicationContext());
recyclerView?.setLayoutManager(mLayoutManager);
recyclerView?.setItemAnimator(DefaultItemAnimator());
recyclerView?.addItemDecoration(MyDividerItemDecoration(this, LinearLayoutManager.VERTICAL, 16));
recyclerView?.setAdapter(mAdapter);

/**
* On long press on RecyclerView item, open alert dialog
* with options to choose
* Edit and Delete
* */
recyclerView?.addOnItemTouchListener(RecyclerTouchListener(this, recyclerView!!, object : RecyclerTouchListener.ClickListener
{

override fun onClick(view: View, position: Int)
{
}

override fun onLongClick(view: View, position: Int)
{
showActionsDialog(position);
}
}))

/**
* Check for stored Api Key in shared preferences
* If not present, make api call to register the user
* This will be executed when app is installed for the first time
* or data is cleared from settings
* */

var test = PrefUtils.getApiKey(this)
if (TextUtils.isEmpty(PrefUtils?.getApiKey(this)))
{
registerUser();
} else
{
// user is already registered, fetch all notes
fetchAllNotes();
}
}

override fun onCreateOptionsMenu(menu: Menu): Boolean
{
// Inflate the menu; this adds items to the action bar if it is present.
menuInflater.inflate(R.menu.menu_main, menu)
return true
}

override fun onOptionsItemSelected(item: MenuItem): Boolean
{
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
return when (item.itemId)
{
R.id.action_settings -> true
else -> super.onOptionsItemSelected(item)
}
}


/**
* Registering new user
* sending unique id as device identification
* https://developer.android.com/training/articles/user-data-ids.html
*/
private fun registerUser()
{
// unique id to identify the device
val uniqueId = UUID.randomUUID().toString()

disposable
.add(apiService.register(uniqueId)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(
object : DisposableSingleObserver<User>()
{
override fun onSuccess(user: User)
{
// Storing user API Key in preferences
user.apiKey?.let { PrefUtils.storeApiKey(applicationContext, it) }

Toast.makeText(applicationContext,
"Device is registered successfully! ApiKey: " + PrefUtils.getApiKey(applicationContext),
Toast.LENGTH_LONG).show()
}

override fun onError(e: Throwable)
{
Log.e(TAG, "onError: " + e.message)
showError(e)
}
}))
}

/**
* Fetching all notes from api
* The received items will be in random order
* map() operator is used to sort the items in descending order by Id
*/
fun fetchAllNotes()
{
disposable.add(apiService.fetchAllNotes().subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).map(
object : io.reactivex.functions.Function<List<Note>, List<Note>>
{
override fun apply(notes: List<Note>): List<Note>
{
Collections.sort(notes, object : Comparator<Note>
{
override fun compare(n1: Note?, n2: Note?): Int
{
return n2!!.id - n1!!.id;

}
})
return notes
}
}).subscribeWith(object : DisposableSingleObserver<List<Note>>()
{
override fun onSuccess(notes: List<Note>)
{
noteList.clear();
noteList.addAll(notes);
mAdapter.notifyDataSetChanged();

toggleEmptyNotes();
}

override fun onError(e: Throwable)
{
Log.e(TAG, "onError: " + e.message);
showError(e);


}
}))
}


/**
* Creating new note
*/
private fun createNote(note: String)
{
disposable.add(apiService.createNote(note)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(
object : DisposableSingleObserver<Note>()
{

override fun onSuccess(note: Note)
{
if (!TextUtils.isEmpty(note.error))
{
Toast.makeText(applicationContext, note.error, Toast.LENGTH_LONG).show()
return
}

Log.d(TAG, "new note created: " + note.id + ", " + note.note + ", " + note.timestamp)

// Add new item and notify adapter
noteList.add(0, note)
mAdapter.notifyItemInserted(0)

toggleEmptyNotes()
}

override fun onError(e: Throwable)
{
Log.e(TAG, "onError: " + e.message)
showError(e)
}
}))
}

/**
* Updating a note
*/
private fun updateNote(noteId: Int, note: String, position: Int)
{
disposable
.add(apiService.updateNote(noteId,
note)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(object :
DisposableCompletableObserver()
{
override fun onComplete()
{
Log.d(TAG, "Note updated!")

val n = noteList.get(position)
n.note = (note)

// Update item and notify adapter
noteList.set(position, n)
mAdapter.notifyItemChanged(position)
}

override fun onError(e: Throwable)
{
Log.e(TAG, "onError: " + e.message)
showError(e)
}
}))
}

/**
* Deleting a note
*/
private fun deleteNote(noteId: Int, position: Int)
{
Log.e(TAG, "deleteNote: $noteId, $position")
disposable
.add(apiService.deleteNote(noteId)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(
object : DisposableCompletableObserver()
{
override fun onComplete()
{
Log.d(TAG, "Note deleted! $noteId")

// Remove and notify adapter about item deletion
noteList.removeAt(position)
mAdapter.notifyItemRemoved(position)

Toast.makeText(this@MainActivity, "Note deleted!", Toast.LENGTH_SHORT).show()

toggleEmptyNotes()
}

override fun onError(e: Throwable)
{
Log.e(TAG, "onError: " + e.message)
showError(e)
}
}))
}

/**
* Shows alert dialog with EditText options to enter / edit
* a note.
* when shouldUpdate=true, it automatically displays old note and changes the
* button text to UPDATE
*/
private fun showNoteDialog(shouldUpdate: Boolean, note: Note?, position: Int)
{
val layoutInflaterAndroid = LayoutInflater.from(applicationContext)
val view = layoutInflaterAndroid.inflate(R.layout.note_dialog, null)

val alertDialogBuilderUserInput = AlertDialog.Builder(this@MainActivity)
alertDialogBuilderUserInput.setView(view)

val inputNote = view.findViewById<EditText>(R.id.note)
val dialogTitle = view.findViewById<TextView>(R.id.dialog_title)
dialogTitle.setText(if (!shouldUpdate) getString(R.string.lbl_new_note_title) else getString(R.string.lbl_edit_note_title))

if (shouldUpdate && note != null)
{
inputNote.setText(note.note)
}
alertDialogBuilderUserInput.setCancelable(false).setPositiveButton(if (shouldUpdate) "update" else "save",
DialogInterface.OnClickListener { dialogBox, id -> })
.setNegativeButton("cancel", DialogInterface.OnClickListener { dialogBox, id -> dialogBox.cancel() })

val alertDialog = alertDialogBuilderUserInput.create()
alertDialog.show()

alertDialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(View.OnClickListener {
// Show toast message when no text is entered
if (TextUtils.isEmpty(inputNote.text.toString()))
{
Toast.makeText(this@MainActivity, "Enter note!", Toast.LENGTH_SHORT).show()
return@OnClickListener
} else
{
alertDialog.dismiss()
}

// check if user updating note
if (shouldUpdate && note != null)
{
// update note by it's id
updateNote(note.id, inputNote.text.toString(), position)
} else
{
// create new note
createNote(inputNote.text.toString())
}
})
}

/**
* Opens dialog with Edit - Delete options
* Edit - 0
* Delete - 0
*/
private fun showActionsDialog(position: Int)
{
val colors = arrayOf<CharSequence>("Edit", "Delete")

val builder = AlertDialog.Builder(this)
builder.setTitle("Choose option")
builder.setItems(colors) { dialog, which ->
if (which == 0)
{
showNoteDialog(true, noteList.get(position), position)
} else
{
deleteNote(noteList.get(position).id, position)
}
}
builder.show()
}

private fun toggleEmptyNotes()
{
if (noteList.size > 0)
{
noNotesView?.setVisibility(View.GONE)
} else
{
noNotesView?.setVisibility(View.VISIBLE)
}
}


/**
* Showing a Snackbar with error message
* The error body will be in json format
* {"error": "Error message!"}
*/
fun showError(e: Throwable)
{
var message = ""
try
{
if (e is IOException)
{
message = "No internet connection!"
}
else (e is HttpException)
run {
var error = e as HttpException
var errorBody = error.response().errorBody().toString()
var jObj = JSONObject(errorBody)
message = jObj.getString("error")



}

}
catch (e1: IOException)
{
e1.printStackTrace()
}
catch (e1: JSONException)
{
e1.printStackTrace()
}
catch (e1: Exception)
{
e1.printStackTrace()
}

if (TextUtils.isEmpty(message))
{
message = "Unknown error occurred! Check LogCat."
}

val snackbar = coordinatorLayout?.let { Snackbar.make(it, message, Snackbar.LENGTH_LONG) }

val sbView = snackbar?.getView()
val textView = sbView?.findViewById<TextView>(android.support.design.R.id.snackbar_text)
textView?.setTextColor(Color.YELLOW)
snackbar?.show()
}

fun whiteNotificationBar(view: View)
{
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
{
var flags = view.getSystemUiVisibility()
flags = View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
view.setSystemUiVisibility(flags)
getWindow().setStatusBarColor(Color.WHITE)
}
}

override fun onDestroy()
{
super.onDestroy()
disposable.dispose()
}
}


ApiService Interface



interface  ApiService
{
// Register new user
@FormUrlEncoded
@POST("notes/user/register")
fun register(@Field("device_id") deviceId: String): Single<User>
// Single<User> register(@Field("device_id") String deviceId)

// Create note
@FormUrlEncoded
@POST("notes/new")
fun createNote(@Field("note") note: String): Single<Note>

// Fetch all notes
@GET("notes/all") fun fetchAllNotes(): Single<List<Note>>

// Update single note
@FormUrlEncoded
@PUT("notes/{id}")
fun updateNote(@Path("id") noteId: Int, @Field("note") note: String): Completable

// Delete note
@DELETE("notes/{id}")
fun deleteNote(@Path("id") noteId: Int): Completable
}


ApiClient Class



class ApiClient
{
companion object
{
var retrofit: Retrofit? = null
var REQUEST_TIMEOUT = 60
var okHttpClient: OkHttpClient? = null

fun getClient(context: Context): Retrofit?
{
if (okHttpClient == null)
initOkHttp(context)

if (retrofit == null)
{
retrofit = Retrofit.Builder().baseUrl(BASE_URL).client(okHttpClient)
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create()).build()
}
return retrofit
}

fun initOkHttp(context: Context)
{
val httpClient = OkHttpClient().newBuilder().connectTimeout(REQUEST_TIMEOUT.toLong(), TimeUnit.SECONDS)
.readTimeout(REQUEST_TIMEOUT.toLong(), TimeUnit.SECONDS)
.writeTimeout(REQUEST_TIMEOUT.toLong(), TimeUnit.SECONDS)

val interceptor = HttpLoggingInterceptor()
interceptor.level = HttpLoggingInterceptor.Level.BODY

httpClient.addInterceptor(interceptor)

httpClient.addInterceptor(object : Interceptor
{
override fun intercept(chain: Interceptor.Chain): Response
{
var original = chain.request()
var requestBuilder = original.newBuilder()

.addHeader("Accept", "application/json").addHeader("Content-Type", "application/json");

// Adding Authorization token (API Key)
// Requests will be denied without API key
if (!TextUtils.isEmpty(PrefUtils.getApiKey(context)))
{
requestBuilder.addHeader("Authorization", PrefUtils.getApiKey(context));
}

var request = requestBuilder.build();

return chain.proceed(request)
}


})

okHttpClient = httpClient.build()
}
}
}


PrefUtils Class



 class PrefUtils
{
/**
* Storing API Key in shared preferences to
* add it in header part of every retrofit request
*/


companion object
{

fun getSharedPreferences(context: Context): SharedPreferences
{
return context.getSharedPreferences("APP_PREF", Context.MODE_PRIVATE)
}

fun storeApiKey(context: Context, apiKey: String)
{
val editor = getSharedPreferences(context).edit()
editor.putString("API_KEY", apiKey)
editor.commit()
}

fun getApiKey(context: Context): String?
{
return getSharedPreferences(context).getString("API_KEY", null)
}
}
}









share|improve this question















I have converted RAVI TAMADA's RxJava tutorial from Java into Kotlin Android RxJava Networking with Retrofit, Gson – Notes App. On testing of the application just on the first network call I receive a HTTP 500 Internal Error (com.jakewharton.retrofit2.adapter.rxjava2.HttpException: HTTP 500 Internal Server Error). This is for the first network call registerUsers As far as conversion is concerned I have done everything by the book.



I have included the code base for the following classes



MainActivity



class MainActivity : AppCompatActivity()
{

lateinit var apiService: ApiService
var disposable = CompositeDisposable()
lateinit var mAdapter: NotesAdapter
var noteList = ArrayList<Note>()

companion object
{
val TAG = MainActivity::class.java.simpleName;

}

@BindView(R.id.coordinator_layout) var coordinatorLayout: CoordinatorLayout? = null

@BindView(R.id.recycler_view) var recyclerView: RecyclerView? = null

@BindView(R.id.txt_empty_notes_view) var noNotesView: TextView? = null

override fun onCreate(savedInstanceState: Bundle?)
{
super.onCreate(savedInstanceState)

getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);

val toolbar = findViewById<Toolbar>(R.id.toolbar)
toolbar.setTitle(getString(R.string.activity_title_home))
setSupportActionBar(toolbar)

fab.setOnClickListener { view ->

showNoteDialog(false, null, -1);

}

// white background notification bar
whiteNotificationBar(fab);

apiService = ApiClient.getClient(getApplicationContext())?.create(ApiService::class.java)!!

mAdapter = NotesAdapter(this, noteList)
var mLayoutManager = LinearLayoutManager(getApplicationContext());
recyclerView?.setLayoutManager(mLayoutManager);
recyclerView?.setItemAnimator(DefaultItemAnimator());
recyclerView?.addItemDecoration(MyDividerItemDecoration(this, LinearLayoutManager.VERTICAL, 16));
recyclerView?.setAdapter(mAdapter);

/**
* On long press on RecyclerView item, open alert dialog
* with options to choose
* Edit and Delete
* */
recyclerView?.addOnItemTouchListener(RecyclerTouchListener(this, recyclerView!!, object : RecyclerTouchListener.ClickListener
{

override fun onClick(view: View, position: Int)
{
}

override fun onLongClick(view: View, position: Int)
{
showActionsDialog(position);
}
}))

/**
* Check for stored Api Key in shared preferences
* If not present, make api call to register the user
* This will be executed when app is installed for the first time
* or data is cleared from settings
* */

var test = PrefUtils.getApiKey(this)
if (TextUtils.isEmpty(PrefUtils?.getApiKey(this)))
{
registerUser();
} else
{
// user is already registered, fetch all notes
fetchAllNotes();
}
}

override fun onCreateOptionsMenu(menu: Menu): Boolean
{
// Inflate the menu; this adds items to the action bar if it is present.
menuInflater.inflate(R.menu.menu_main, menu)
return true
}

override fun onOptionsItemSelected(item: MenuItem): Boolean
{
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
return when (item.itemId)
{
R.id.action_settings -> true
else -> super.onOptionsItemSelected(item)
}
}


/**
* Registering new user
* sending unique id as device identification
* https://developer.android.com/training/articles/user-data-ids.html
*/
private fun registerUser()
{
// unique id to identify the device
val uniqueId = UUID.randomUUID().toString()

disposable
.add(apiService.register(uniqueId)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(
object : DisposableSingleObserver<User>()
{
override fun onSuccess(user: User)
{
// Storing user API Key in preferences
user.apiKey?.let { PrefUtils.storeApiKey(applicationContext, it) }

Toast.makeText(applicationContext,
"Device is registered successfully! ApiKey: " + PrefUtils.getApiKey(applicationContext),
Toast.LENGTH_LONG).show()
}

override fun onError(e: Throwable)
{
Log.e(TAG, "onError: " + e.message)
showError(e)
}
}))
}

/**
* Fetching all notes from api
* The received items will be in random order
* map() operator is used to sort the items in descending order by Id
*/
fun fetchAllNotes()
{
disposable.add(apiService.fetchAllNotes().subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).map(
object : io.reactivex.functions.Function<List<Note>, List<Note>>
{
override fun apply(notes: List<Note>): List<Note>
{
Collections.sort(notes, object : Comparator<Note>
{
override fun compare(n1: Note?, n2: Note?): Int
{
return n2!!.id - n1!!.id;

}
})
return notes
}
}).subscribeWith(object : DisposableSingleObserver<List<Note>>()
{
override fun onSuccess(notes: List<Note>)
{
noteList.clear();
noteList.addAll(notes);
mAdapter.notifyDataSetChanged();

toggleEmptyNotes();
}

override fun onError(e: Throwable)
{
Log.e(TAG, "onError: " + e.message);
showError(e);


}
}))
}


/**
* Creating new note
*/
private fun createNote(note: String)
{
disposable.add(apiService.createNote(note)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(
object : DisposableSingleObserver<Note>()
{

override fun onSuccess(note: Note)
{
if (!TextUtils.isEmpty(note.error))
{
Toast.makeText(applicationContext, note.error, Toast.LENGTH_LONG).show()
return
}

Log.d(TAG, "new note created: " + note.id + ", " + note.note + ", " + note.timestamp)

// Add new item and notify adapter
noteList.add(0, note)
mAdapter.notifyItemInserted(0)

toggleEmptyNotes()
}

override fun onError(e: Throwable)
{
Log.e(TAG, "onError: " + e.message)
showError(e)
}
}))
}

/**
* Updating a note
*/
private fun updateNote(noteId: Int, note: String, position: Int)
{
disposable
.add(apiService.updateNote(noteId,
note)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(object :
DisposableCompletableObserver()
{
override fun onComplete()
{
Log.d(TAG, "Note updated!")

val n = noteList.get(position)
n.note = (note)

// Update item and notify adapter
noteList.set(position, n)
mAdapter.notifyItemChanged(position)
}

override fun onError(e: Throwable)
{
Log.e(TAG, "onError: " + e.message)
showError(e)
}
}))
}

/**
* Deleting a note
*/
private fun deleteNote(noteId: Int, position: Int)
{
Log.e(TAG, "deleteNote: $noteId, $position")
disposable
.add(apiService.deleteNote(noteId)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(
object : DisposableCompletableObserver()
{
override fun onComplete()
{
Log.d(TAG, "Note deleted! $noteId")

// Remove and notify adapter about item deletion
noteList.removeAt(position)
mAdapter.notifyItemRemoved(position)

Toast.makeText(this@MainActivity, "Note deleted!", Toast.LENGTH_SHORT).show()

toggleEmptyNotes()
}

override fun onError(e: Throwable)
{
Log.e(TAG, "onError: " + e.message)
showError(e)
}
}))
}

/**
* Shows alert dialog with EditText options to enter / edit
* a note.
* when shouldUpdate=true, it automatically displays old note and changes the
* button text to UPDATE
*/
private fun showNoteDialog(shouldUpdate: Boolean, note: Note?, position: Int)
{
val layoutInflaterAndroid = LayoutInflater.from(applicationContext)
val view = layoutInflaterAndroid.inflate(R.layout.note_dialog, null)

val alertDialogBuilderUserInput = AlertDialog.Builder(this@MainActivity)
alertDialogBuilderUserInput.setView(view)

val inputNote = view.findViewById<EditText>(R.id.note)
val dialogTitle = view.findViewById<TextView>(R.id.dialog_title)
dialogTitle.setText(if (!shouldUpdate) getString(R.string.lbl_new_note_title) else getString(R.string.lbl_edit_note_title))

if (shouldUpdate && note != null)
{
inputNote.setText(note.note)
}
alertDialogBuilderUserInput.setCancelable(false).setPositiveButton(if (shouldUpdate) "update" else "save",
DialogInterface.OnClickListener { dialogBox, id -> })
.setNegativeButton("cancel", DialogInterface.OnClickListener { dialogBox, id -> dialogBox.cancel() })

val alertDialog = alertDialogBuilderUserInput.create()
alertDialog.show()

alertDialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(View.OnClickListener {
// Show toast message when no text is entered
if (TextUtils.isEmpty(inputNote.text.toString()))
{
Toast.makeText(this@MainActivity, "Enter note!", Toast.LENGTH_SHORT).show()
return@OnClickListener
} else
{
alertDialog.dismiss()
}

// check if user updating note
if (shouldUpdate && note != null)
{
// update note by it's id
updateNote(note.id, inputNote.text.toString(), position)
} else
{
// create new note
createNote(inputNote.text.toString())
}
})
}

/**
* Opens dialog with Edit - Delete options
* Edit - 0
* Delete - 0
*/
private fun showActionsDialog(position: Int)
{
val colors = arrayOf<CharSequence>("Edit", "Delete")

val builder = AlertDialog.Builder(this)
builder.setTitle("Choose option")
builder.setItems(colors) { dialog, which ->
if (which == 0)
{
showNoteDialog(true, noteList.get(position), position)
} else
{
deleteNote(noteList.get(position).id, position)
}
}
builder.show()
}

private fun toggleEmptyNotes()
{
if (noteList.size > 0)
{
noNotesView?.setVisibility(View.GONE)
} else
{
noNotesView?.setVisibility(View.VISIBLE)
}
}


/**
* Showing a Snackbar with error message
* The error body will be in json format
* {"error": "Error message!"}
*/
fun showError(e: Throwable)
{
var message = ""
try
{
if (e is IOException)
{
message = "No internet connection!"
}
else (e is HttpException)
run {
var error = e as HttpException
var errorBody = error.response().errorBody().toString()
var jObj = JSONObject(errorBody)
message = jObj.getString("error")



}

}
catch (e1: IOException)
{
e1.printStackTrace()
}
catch (e1: JSONException)
{
e1.printStackTrace()
}
catch (e1: Exception)
{
e1.printStackTrace()
}

if (TextUtils.isEmpty(message))
{
message = "Unknown error occurred! Check LogCat."
}

val snackbar = coordinatorLayout?.let { Snackbar.make(it, message, Snackbar.LENGTH_LONG) }

val sbView = snackbar?.getView()
val textView = sbView?.findViewById<TextView>(android.support.design.R.id.snackbar_text)
textView?.setTextColor(Color.YELLOW)
snackbar?.show()
}

fun whiteNotificationBar(view: View)
{
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
{
var flags = view.getSystemUiVisibility()
flags = View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
view.setSystemUiVisibility(flags)
getWindow().setStatusBarColor(Color.WHITE)
}
}

override fun onDestroy()
{
super.onDestroy()
disposable.dispose()
}
}


ApiService Interface



interface  ApiService
{
// Register new user
@FormUrlEncoded
@POST("notes/user/register")
fun register(@Field("device_id") deviceId: String): Single<User>
// Single<User> register(@Field("device_id") String deviceId)

// Create note
@FormUrlEncoded
@POST("notes/new")
fun createNote(@Field("note") note: String): Single<Note>

// Fetch all notes
@GET("notes/all") fun fetchAllNotes(): Single<List<Note>>

// Update single note
@FormUrlEncoded
@PUT("notes/{id}")
fun updateNote(@Path("id") noteId: Int, @Field("note") note: String): Completable

// Delete note
@DELETE("notes/{id}")
fun deleteNote(@Path("id") noteId: Int): Completable
}


ApiClient Class



class ApiClient
{
companion object
{
var retrofit: Retrofit? = null
var REQUEST_TIMEOUT = 60
var okHttpClient: OkHttpClient? = null

fun getClient(context: Context): Retrofit?
{
if (okHttpClient == null)
initOkHttp(context)

if (retrofit == null)
{
retrofit = Retrofit.Builder().baseUrl(BASE_URL).client(okHttpClient)
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create()).build()
}
return retrofit
}

fun initOkHttp(context: Context)
{
val httpClient = OkHttpClient().newBuilder().connectTimeout(REQUEST_TIMEOUT.toLong(), TimeUnit.SECONDS)
.readTimeout(REQUEST_TIMEOUT.toLong(), TimeUnit.SECONDS)
.writeTimeout(REQUEST_TIMEOUT.toLong(), TimeUnit.SECONDS)

val interceptor = HttpLoggingInterceptor()
interceptor.level = HttpLoggingInterceptor.Level.BODY

httpClient.addInterceptor(interceptor)

httpClient.addInterceptor(object : Interceptor
{
override fun intercept(chain: Interceptor.Chain): Response
{
var original = chain.request()
var requestBuilder = original.newBuilder()

.addHeader("Accept", "application/json").addHeader("Content-Type", "application/json");

// Adding Authorization token (API Key)
// Requests will be denied without API key
if (!TextUtils.isEmpty(PrefUtils.getApiKey(context)))
{
requestBuilder.addHeader("Authorization", PrefUtils.getApiKey(context));
}

var request = requestBuilder.build();

return chain.proceed(request)
}


})

okHttpClient = httpClient.build()
}
}
}


PrefUtils Class



 class PrefUtils
{
/**
* Storing API Key in shared preferences to
* add it in header part of every retrofit request
*/


companion object
{

fun getSharedPreferences(context: Context): SharedPreferences
{
return context.getSharedPreferences("APP_PREF", Context.MODE_PRIVATE)
}

fun storeApiKey(context: Context, apiKey: String)
{
val editor = getSharedPreferences(context).edit()
editor.putString("API_KEY", apiKey)
editor.commit()
}

fun getApiKey(context: Context): String?
{
return getSharedPreferences(context).getString("API_KEY", null)
}
}
}






java android-studio kotlin rx-java rx-java2






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Nov 20 at 12:45

























asked Nov 20 at 12:26









George

428




428








  • 1




    all of the code is irrelevant. 500 means there is an error on the server. Check the server logs. We cannot help you
    – Tim Castelijns
    Nov 20 at 12:49














  • 1




    all of the code is irrelevant. 500 means there is an error on the server. Check the server logs. We cannot help you
    – Tim Castelijns
    Nov 20 at 12:49








1




1




all of the code is irrelevant. 500 means there is an error on the server. Check the server logs. We cannot help you
– Tim Castelijns
Nov 20 at 12:49




all of the code is irrelevant. 500 means there is an error on the server. Check the server logs. We cannot help you
– Tim Castelijns
Nov 20 at 12:49

















active

oldest

votes











Your Answer






StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");

StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});

function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});


}
});














draft saved

draft discarded


















StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53392973%2frxjava-http-500-internal-server%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown






























active

oldest

votes













active

oldest

votes









active

oldest

votes






active

oldest

votes
















draft saved

draft discarded




















































Thanks for contributing an answer to Stack Overflow!


  • Please be sure to answer the question. Provide details and share your research!

But avoid



  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.


To learn more, see our tips on writing great answers.





Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


Please pay close attention to the following guidance:


  • Please be sure to answer the question. Provide details and share your research!

But avoid



  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.


To learn more, see our tips on writing great answers.




draft saved


draft discarded














StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53392973%2frxjava-http-500-internal-server%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown





















































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown

































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown







Popular posts from this blog

404 Error Contact Form 7 ajax form submitting

How to know if a Active Directory user can login interactively

TypeError: fit_transform() missing 1 required positional argument: 'X'