001/* 002 * Stallion Core: A Modern Web Framework 003 * 004 * Copyright (C) 2015 - 2016 Stallion Software LLC. 005 * 006 * This program is free software: you can redistribute it and/or modify it under the terms of the 007 * GNU General Public License as published by the Free Software Foundation, either version 2 of 008 * the License, or (at your option) any later version. This program is distributed in the hope that 009 * it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 010 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public 011 * License for more details. You should have received a copy of the GNU General Public License 012 * along with this program. If not, see <http://www.gnu.org/licenses/gpl-2.0.html>. 013 * 014 * 015 * 016 */ 017 018package io.stallion.dataAccess; 019 020import io.stallion.Context; 021import io.stallion.dataAccess.db.DB; 022import io.stallion.dataAccess.file.JsonFilePersister; 023import io.stallion.exceptions.ConfigException; 024import io.stallion.exceptions.UsageException; 025import org.apache.commons.lang3.StringUtils; 026 027import static io.stallion.utils.Literals.empty; 028 029public class DataAccessRegistration { 030 private Class<? extends ModelController> controllerClass; 031 private Class<? extends Model> modelClass; 032 private Class<? extends Persister> persisterClass; 033 private Class<? extends Stash> stashClass; 034 private boolean syncAllToMemory = true; 035 private DynamicModelDefinition dynamicModelDefinition; 036 private String path; 037 private String relativePath; 038 private String absolutePath; 039 private String tableName; 040 041 private boolean writable = false; 042 private boolean shouldWatch = true; 043 private String nameSpace; 044 private boolean useDataFolder = true; 045 private String templatePath = ""; 046 private String bucket = null; 047 private boolean multiplePerFile = false; 048 private String itemArrayName = ""; 049 private boolean databaseBacked = false; 050 051 /** 052 * Ensure all required fields are set and valid, hydrate any other fields with 053 * defaults. 054 * 055 * @param appTargetPath 056 * @return 057 */ 058 public DataAccessRegistration build(String appTargetPath) { 059 if (controllerClass == null) { 060 throw new ConfigException("You must choose a controller class for every DalRegistration"); 061 } 062 ModelController controller; 063 try { 064 controller = controllerClass.newInstance(); 065 } catch (InstantiationException e) { 066 throw new RuntimeException(e); 067 } catch (IllegalAccessException e) { 068 throw new RuntimeException(e); 069 } 070 if (modelClass == null) { 071 modelClass = controller.getModelClass(); 072 } 073 if (modelClass == null) { 074 throw new ConfigException("You must either set the DalRegistration modelClass property, or the controller must return the model class"); 075 } 076 if (persisterClass == null) { 077 if (DB.available()) { 078 persisterClass = DB.instance().getDefaultPersisterClass(); 079 } else { 080 persisterClass = JsonFilePersister.class; 081 } 082 } 083 if (stashClass == null) { 084 if (syncAllToMemory) { 085 stashClass = LocalMemoryStash.class; 086 } else { 087 stashClass = NoStash.class; 088 } 089 } 090 091 if (empty(tableName)) { 092 tableName = getBucket(); 093 } 094 if (empty(getPath())) { 095 setPath(getBucket()); 096 } 097 098 hydratePaths(appTargetPath); 099 100 101 return this; 102 } 103 104 /** 105 * Parse the target path and hydrate the absolute and relative path fields. 106 * @param appTargetPath 107 */ 108 public void hydratePaths(String appTargetPath) { 109 if (empty(getPath()) && empty(getTableName())) { 110 throw new UsageException("DalRegistration must have a non-empty path or tableName"); 111 } else if (empty(getPath())) { 112 return; 113 } 114 if (getPath().contains("|")) { 115 String[] parts = getPath().split("\\|", 2); 116 setPath(parts[0]); 117 setItemArrayName(parts[1]); 118 setMultiplePerFile(true); 119 } 120 121 if (getPath().startsWith("/")) { 122 setAbsolutePath(getPath()); 123 setRelativePath(getAbsolutePath().replace(appTargetPath, "")); 124 if (relativePath.startsWith("/")) { 125 relativePath = relativePath.substring(1); 126 } 127 } else { 128 if (!StringUtils.isEmpty(nameSpace)) { 129 path = nameSpace + "-" + path; 130 } 131 if (isUseDataFolder()) { 132 setAbsolutePath(Context.getSettings().getDataDirectory() + "/" + getPath()); 133 } else { 134 setAbsolutePath(appTargetPath + "/" + getPath()); 135 } 136 setRelativePath(getPath()); 137 } 138 } 139 140 public Class<? extends ModelController> getControllerClass() { 141 return controllerClass; 142 } 143 144 /** 145 * The ModelController subclass to be associated with this registration 146 * @return 147 */ 148 public DataAccessRegistration setControllerClass(Class<? extends ModelController> controllerClass) { 149 this.controllerClass = controllerClass; 150 return this; 151 } 152 153 public Class<? extends Model> getModelClass() { 154 return modelClass; 155 } 156 157 /** 158 * The data model to be used with this registration 159 * @return 160 */ 161 public DataAccessRegistration setModelClass(Class<? extends Model> modelClass) { 162 this.modelClass = modelClass; 163 return this; 164 } 165 166 public String getPath() { 167 return path; 168 } 169 170 /** 171 * For file based registrations, the path to the folder with the data. Should be relative. 172 * 173 * @return 174 */ 175 public DataAccessRegistration setPath(String path) { 176 this.path = path; 177 return this; 178 } 179 180 public String getRelativePath() { 181 return relativePath; 182 } 183 184 /** 185 * The path to the data, relative to the Settings.targetPath or relative to the 186 * app-data path, if isUseDataFolder() is true. 187 * @return 188 */ 189 DataAccessRegistration setRelativePath(String relativePath) { 190 this.relativePath = relativePath; 191 return this; 192 } 193 194 public String getAbsolutePath() { 195 return absolutePath; 196 } 197 198 /** 199 * The absolute file system path to the data 200 * @return 201 */ 202 DataAccessRegistration setAbsolutePath(String absolutePath) { 203 this.absolutePath = absolutePath; 204 return this; 205 } 206 207 public String getTableName() { 208 return tableName; 209 } 210 211 /** 212 * The database table for the data, for DB backed registrations 213 * @return 214 */ 215 public DataAccessRegistration setTableName(String tableName) { 216 this.tableName = tableName; 217 return this; 218 } 219 220 public Class<? extends Persister> getPersisterClass() { 221 return persisterClass; 222 } 223 224 /** 225 * The Persister subclass to use for this registration 226 * @return 227 */ 228 public DataAccessRegistration setPersisterClass(Class<? extends Persister> persisterClass) { 229 this.persisterClass = persisterClass; 230 return this; 231 } 232 233 public boolean isWritable() { 234 return writable; 235 } 236 237 /** 238 * Is Stallion allowed to write to the datastore, or is it read only? 239 * @param writable 240 * @return 241 */ 242 public DataAccessRegistration setWritable(boolean writable) { 243 this.writable = writable; 244 return this; 245 } 246 247 public boolean isShouldWatch() { 248 return shouldWatch; 249 } 250 251 /** 252 * Should the file system be watched for changes, and reload changes automatically? 253 * 254 * @return 255 */ 256 public DataAccessRegistration setShouldWatch(boolean shouldWatch) { 257 this.shouldWatch = shouldWatch; 258 return this; 259 } 260 261 public String getNameSpace() { 262 return nameSpace; 263 } 264 265 public DataAccessRegistration setNameSpace(String nameSpace) { 266 this.nameSpace = nameSpace; 267 return this; 268 } 269 270 /** 271 * The bucket is the shorthand by which the controller will be accessed from templates, 272 * from DalRegistry.get(), etc. Usually this should be the table name or the folder name, 273 * but you can set it to a custom value. It should be unique among all registrations. 274 * 275 * @return 276 */ 277 public String getBucket() { 278 if (!empty(bucket)) { 279 return bucket; 280 } else if (!StringUtils.isEmpty(getTableName())) { 281 return getTableName(); 282 } else { 283 return getRelativePath(); 284 } 285 } 286 287 public String getTemplatePath() { 288 return templatePath; 289 } 290 291 /** 292 * For displayable registrations, the default template to use for rendering the object. 293 * 294 * @return 295 */ 296 public DataAccessRegistration setTemplatePath(String templatePath) { 297 this.templatePath = templatePath; 298 return this; 299 } 300 301 public boolean isUseDataFolder() { 302 return useDataFolder; 303 } 304 305 /** 306 * If true, relative path will be relative to the app-data folder, not the project folder. 307 * @param useDataFolder 308 * @return 309 */ 310 public DataAccessRegistration setUseDataFolder(boolean useDataFolder) { 311 this.useDataFolder = useDataFolder; 312 return this; 313 } 314 315 /** 316 * The bucket is the shorthand by which the controller will be accessed from templates, 317 * from DalRegistry.get(), etc. Usually this should be the table name or the folder name, 318 * but you can set it to a custom value. It should be unique among all registrations. 319 * 320 * @return 321 */ 322 public DataAccessRegistration setBucket(String bucket) { 323 this.bucket = bucket; 324 return this; 325 } 326 327 public DynamicModelDefinition getDynamicModelDefinition() { 328 return dynamicModelDefinition; 329 } 330 331 332 /** 333 * Used for defining a model via Javascript 334 * 335 * @param dynamicModelDefinition 336 * @return 337 */ 338 public DataAccessRegistration setDynamicModelDefinition(DynamicModelDefinition dynamicModelDefinition) { 339 this.dynamicModelDefinition = dynamicModelDefinition; 340 return this; 341 } 342 343 public boolean isMultiplePerFile() { 344 return multiplePerFile; 345 } 346 347 /** 348 * True for file-based data stores if there are multiple objects per file, instead of one file per object 349 * @return 350 */ 351 public DataAccessRegistration setMultiplePerFile(boolean multiplePerFile) { 352 this.multiplePerFile = multiplePerFile; 353 return this; 354 } 355 356 public String getItemArrayName() { 357 return itemArrayName; 358 } 359 360 /** 361 * If isMultiplePerFile() is true, this is the name of the array of the objects. 362 * @param itemArrayName 363 * @return 364 */ 365 public DataAccessRegistration setItemArrayName(String itemArrayName) { 366 this.itemArrayName = itemArrayName; 367 return this; 368 } 369 370 /** 371 * The class to use for the data stash. Default is LocalMemoryStash, which syncs 372 * everything into local memory. Use NoStash to avoid syncing into memory. 373 * @return 374 */ 375 public Class<? extends Stash> getStashClass() { 376 return stashClass; 377 } 378 379 public DataAccessRegistration setStashClass(Class<? extends Stash> stashClass) { 380 this.stashClass = stashClass; 381 return this; 382 } 383 384 public boolean isSyncAllToMemory() { 385 return syncAllToMemory; 386 } 387 388 /** 389 * Should all objects from the data store be synced to local memory? Defaults to true. 390 * @return 391 */ 392 public DataAccessRegistration setSyncAllToMemory(boolean syncAllToMemory) { 393 this.syncAllToMemory = syncAllToMemory; 394 return this; 395 } 396 397 398 public boolean isDatabaseBacked() { 399 return databaseBacked; 400 } 401 402 /** 403 * True if the registration should use the database, false if it should use the 404 * file system. 405 * 406 * @param databaseBacked 407 * @return 408 */ 409 public DataAccessRegistration setDatabaseBacked(boolean databaseBacked) { 410 this.databaseBacked = databaseBacked; 411 return this; 412 } 413 414} 415