Changeset View
Changeset View
Standalone View
Standalone View
src/main/java/net/wildfyre/users/Users.kt
- This file was added.
/* | |||||
* Copyright 2019 Wildfyre.net | |||||
* | |||||
* Licensed under the Apache License, Version 2.0 (the "License"); | |||||
* you may not use this file except in compliance with the License. | |||||
* You may obtain a copy of the License at | |||||
* | |||||
* http://www.apache.org/licenses/LICENSE-2.0 | |||||
* | |||||
* Unless required by applicable law or agreed to in writing, software | |||||
* distributed under the License is distributed on an "AS IS" BASIS, | |||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
* See the License for the specific language governing permissions and | |||||
* limitations under the License. | |||||
*/ | |||||
package net.wildfyre.users | |||||
import com.eclipsesource.json.JsonObject | |||||
import com.eclipsesource.json.WriterConfig | |||||
import net.wildfyre.api.Internal | |||||
import net.wildfyre.descriptors.CacheManager | |||||
import net.wildfyre.descriptors.NoSuchEntityException | |||||
import net.wildfyre.http.IssueInTransferException | |||||
import net.wildfyre.http.Method.GET | |||||
import net.wildfyre.http.Request | |||||
import net.wildfyre.utils.LazyMap | |||||
import net.wildfyre.utils.ProgrammingException | |||||
object Users { | |||||
//region Attributes | |||||
internal lateinit var users: MutableMap<Int, User> // package instead of private, to enable access from User | |||||
private var userId: Int = 0 | |||||
//endregion | |||||
//region Expiration | |||||
private val manager = CacheManager().setExpirationTime((1000 * 60 * 30).toLong()) // 30 minutes | |||||
//endregion | |||||
//region Queries | |||||
/** | |||||
* Retrieves a user from the database. | |||||
* | |||||
* If the user is in the cache, it is returned. If the user is not in the cache, this method stalls and queries | |||||
* the server. If the user is in the cache but has expired, it is returned and a concurrent update job starts. | |||||
* | |||||
* Note that this API is written so that IDs are not needed for the typical user. | |||||
* | |||||
* This method intentionally doesn't throw any exceptions, because it is designed to be used in a concurrent | |||||
* fashion. If any occurs, they are sent to the exceptions handlers, see | |||||
* [Internal.setCantConnectHandler]. | |||||
* | |||||
* @param id the ID of the user. | |||||
* @return The user, if any was found. | |||||
* @see getCached | |||||
*/ | |||||
operator fun get(id: Int): User? { | |||||
val user = Users.getCached(id) | |||||
?: User.create(id) | |||||
// There is no user in the cache, stall & query server | |||||
try { | |||||
if (user.isNew) { | |||||
users[id] = user | |||||
user.update() // in this thread | |||||
} | |||||
} catch (e: NoSuchEntityException) { | |||||
return null // there is no such user server-side | |||||
} catch (e: Request.CantConnectException) { | |||||
Internal.throwCantConnect(e) | |||||
return null | |||||
} | |||||
// There is a user in the cache, but it's expired | |||||
if (!user.isValid) | |||||
Internal.submitUpdate(user) // in a new thread | |||||
user.use() | |||||
return user | |||||
} | |||||
/** | |||||
* Retrieves the user corresponding to a provided ID **from the cache**. This method is not designed to be used | |||||
* often, as it is only of any use in the rare case where the lib needs to access the cache itself. Therefore, it | |||||
* has a few shortcomings, listed below. If this does not exactly fit what you're searching for, you should see | |||||
* [get]. | |||||
* | |||||
* This method will not query the server in any way. It only returns the contents of the cache for that specific | |||||
* ID. | |||||
* | |||||
* As a side effect, this method will not update the user either. This means that the User's inherent timer | |||||
* (see the [Descriptor][net.wildfyre.descriptors.Descriptor] class for more information) will not be reset, as | |||||
* this does not count as a usage of the User. | |||||
* | |||||
* @param id the ID of the user. | |||||
* @return The user, or an empty optional if it is not found in the cache. | |||||
* @see get | |||||
*/ | |||||
fun getCached(id: Int): User? { | |||||
return users[id] | |||||
} | |||||
/** | |||||
* The user that is connected to the API. | |||||
* @return The user that is connected to the API. | |||||
*/ | |||||
fun me(): LoggedUser { | |||||
return get(userId)?.asLogged() | |||||
?: throw ProgrammingException("It is not possible that the internal ID used to connect to the server" + | |||||
" does not match any User.") | |||||
} | |||||
/** | |||||
* The full User cache is cleared, no User is kept. | |||||
*/ | |||||
fun clear() { | |||||
if (::users.isInitialized) | |||||
users.clear() | |||||
} | |||||
/** | |||||
* Cleans the internal cache, by removing the users that have expired. | |||||
*/ | |||||
fun clean() { | |||||
val time = System.currentTimeMillis() // calling curentTimeMillis once, instead of calling it for every User. | |||||
users.values.removeIf { u -> !u.isValid(time) } | |||||
} | |||||
/** | |||||
* Resets the stored data of this class, that is dependent on the logged-in user. It is necessary to call this | |||||
* method if you'd like to disconnect and reconnect as an other user. | |||||
*/ | |||||
fun reset() { | |||||
userId = -1 | |||||
users.clear() | |||||
} | |||||
/** | |||||
* Prepares this class to serve Users. This method must be called after the token is set, but before any work is | |||||
* done on Areas or Posts. This method must be called again if you modify the token. | |||||
* @throws Request.CantConnectException if no connection to the server can be established. | |||||
*/ | |||||
@Throws(Request.CantConnectException::class) | |||||
fun init() { | |||||
try { | |||||
val json = Request(GET, "/users/") | |||||
.addToken(Internal.token()) | |||||
.getJson() as JsonObject | |||||
userId = json.getInt("user", -1) | |||||
if (userId == -1) | |||||
throw RuntimeException("Couldn't find the ID of the logged-in user!\n" | |||||
+ json.toString(WriterConfig.PRETTY_PRINT)) | |||||
users = LazyMap() | |||||
} catch (e: IssueInTransferException) { | |||||
throw RuntimeException("Couldn't find the ID of the logged-in user.", e) | |||||
} | |||||
} | |||||
//endregion | |||||
//region Getters | |||||
fun isMyID(id: Int): Boolean { | |||||
if (userId == -1) | |||||
System.err.println("Warning: calling Users#isMyID without initializing. Call Users#init or Internal#init.") | |||||
return userId == id | |||||
} | |||||
fun myID(): Int? = if (userId != -1) userId else null | |||||
/** | |||||
* The Cache Manager that handles Users. | |||||
* @return The User Cache Manager. | |||||
*/ | |||||
fun cacheManager(): CacheManager { | |||||
return manager | |||||
} | |||||
//endregion | |||||
} |