Пример #1
1
  private Prop findRelProperty(Class<?> fromCls, String rel, Class<?> toCls) {
    Object entity = !fromCls.isInterface() ? data.constructor.create(fromCls) : null;

    for (Prop prop : Beany.propertiesOf(fromCls).select(data.relPropSelector)) {

      String relName = null;

      if (!fromCls.isInterface()) {
        Object value = prop.getRaw(entity);
        if (hasEntityLinks(value)) {
          EntityLinks links = entityLinks(value);
          relName = links.relationName();
        }
      } else {
        Rel relation = prop.getAnnotation(Rel.class);
        if (relation != null) {
          relName = relation.value();
        }
      }

      if (relName != null && relName.equals(rel)) {
        if (prop.getRawTypeArg(0).equals(toCls)) {
          return prop;
        }
      }
    }

    Log.warn(
        "Didn't find inverse relation property!", "relation", rel, "from", fromCls, "to", toCls);
    return null;
  }
Пример #2
0
  private void resolveDoubleFileInconsistency() {
    String file1 = currentFile().getName();
    String file2 = otherFile().getName();

    Log.warn(
        "The database was left in inconsistent state, both files exist!",
        "file1",
        file1,
        "file2",
        file2);

    long modif1, modif2;
    try {
      modif1 = (Long) loadMetadata(new FileInputStream(currentFile())).get(META_TIMESTAMP);
      modif2 = (Long) loadMetadata(new FileInputStream(otherFile())).get(META_TIMESTAMP);
    } catch (FileNotFoundException e) {
      throw new RuntimeException(e);
    }

    U.must(
        modif1 != modif2,
        "Cannot determine which database file to remove, please remove the incorrect file manually!");

    // delete the most recent file, since it wasn't written completely
    File recent = modif1 > modif2 ? currentFile() : otherFile();

    Log.warn(
        "The more recent database file is assumed incomplete, so it will be deleted!",
        "file",
        recent);

    recent.delete();
  }
Пример #3
0
  @Test
  public void testTCPClientWithDefaultConnections() {
    Log.setLogLevel(LogLevel.DEBUG);

    TCPClient client =
        TCP.client()
            .host("localhost")
            .port(8080)
            .connections(5)
            .protocol(HI_CLIENT)
            .build()
            .start();

    // let the clients wait
    UTILS.sleep(3000);

    TCPServer server = TCP.server().port(8080).protocol(UPPERCASE_SERVER).build().start();

    // let the server serve the clients
    UTILS.sleep(3000);

    eq(client.info().messagesProcessed(), 5);
    eq(server.info().messagesProcessed(), 5);

    client.shutdown();
    server.shutdown();
  }
Пример #4
0
  @Test
  public void testTCPClientWithDefaultAndCustomConnections() {
    Log.setLogLevel(LogLevel.DEBUG);

    TCPClient client =
        TCP.client()
            .host("127.0.0.1")
            .port(8080)
            .connections(3)
            .protocol(HI_CLIENT)
            .build()
            .start();
    client.connect("localhost", 9090, HI_CLIENT, 2, false, null);

    // let the clients wait
    UTILS.sleep(3000);

    TCPServer server1 = TCP.server().port(8080).protocol(UPPERCASE_SERVER).build().start();
    TCPServer server2 = TCP.server().port(9090).protocol(UPPERCASE_SERVER).build().start();

    // let the servers serve the clients
    UTILS.sleep(3000);

    eq(client.info().messagesProcessed(), 5);
    eq(server1.info().messagesProcessed(), 3);
    eq(server2.info().messagesProcessed(), 2);

    client.shutdown();
    server1.shutdown();
    server2.shutdown();
  }
Пример #5
0
  @Test
  public void testAsyncHttpServer() {
    Log.debugging();

    On.req(
        req -> {
          U.must(!req.isAsync());
          req.async();
          U.must(req.isAsync());

          Jobs.after(10, TimeUnit.MILLISECONDS)
              .run(
                  () -> {
                    IO.write(req.out(), "O");

                    Jobs.after(10, TimeUnit.MILLISECONDS)
                        .run(
                            () -> {
                              IO.write(req.out(), "K");
                              req.done();
                            });
                  });

          return req;
        });

    Self.get("/").expect("OK").benchmark(1, 100, 10000);
    Self.post("/").expect("OK").benchmark(1, 100, 10000);
  }
Пример #6
0
  public void delete(long id) {
    sharedLock();

    try {
      failIfReadonlyTx();
      validateId(id);

      Rec old = data.data.get(id);
      Object entity = obj(old);
      secureDelete(entity);

      boolean removed = data.data.remove(id, old);
      occErrorIf(
          !removed, "Concurrent modification occured while deleting the object with ID=%s!", id);

      if (data.insideTx.get()) {
        data.txChanges.putIfAbsent(id, old);
      }

      deleteRelsFor(entity);

      data.lastChangedOn.set(System.currentTimeMillis());

      Log.audit("Deleted DB record", "id", id);

    } finally {
      sharedUnlock();
    }
  }
Пример #7
0
  private RelPair getRelPair(Object entity, Prop prop, String rel, boolean inverse) {

    Class<?> cls = prop.getRawTypeArg(0);
    Class<? extends Object> entCls = Cls.unproxy(entity.getClass());

    Class<?> srcType = inverse ? cls : entCls;
    Class<?> destType = inverse ? entCls : cls;

    Tuple key = new Tuple(rel, srcType, destType);
    RelPair relPair = data.relPairs.get(key);

    Prop srcProp, destProp;

    if (relPair != null) {
      srcProp = relPair.srcProp;
      destProp = relPair.destProp;
    } else {
      String invRel = inverse ? rel : "^" + rel;
      Prop p = findRelProperty(cls, invRel, entCls);
      srcProp = inverse ? p : prop;
      destProp = inverse ? prop : p;

      relPair = new RelPair(rel, srcType, destType, srcProp, destProp);
      data.relPairs.putIfAbsent(key, relPair);

      if (srcType == null || srcProp == null || destType == null || destProp == null) {
        Log.warn("Incomplete relation pair!", "relation", relPair);
      }
    }

    return relPair;
  }
Пример #8
0
  public static void main(String[] args) {

    Map<String, String> cfg = U.map("title", "App", "user", "u1", "theme", "x");

    On.get("/cfg").json(cfg);

    PageMenu menu = PageMenu.from(U.map("Home", "/", "Groups", "/group"));

    On.defaultWrap((data, next) -> next.invoke(x -> page(x).menu(menu).title(cfg.get("title"))));

    List<Btn> letters = Flow.chars('a', 'z').map(c -> btn(c).linkTo("/find?p=" + c)).toList();

    On.page("/")
        .gui("p", p -> U.list(letters, grid(cfg).keyView(k -> a(k).href("/edit?key=" + k))));

    On.page("/group")
        .gui(
            "x",
            x -> {
              return Do.map(Do.group(cfg).by((k, v) -> k.charAt(0)))
                  .toList((k, v) -> U.list(h3(k), grid(v)));
            });

    On.page("/find")
        .gui("p", p -> U.list(letters, grid(Find.allOf(cfg).where((k, v) -> k.startsWith(p)))));

    Btn ok = btn("Update").onClick(() -> Log.info("Updated", "cfg", cfg));

    On.page("/edit").gui("key", key -> edit(cfg, key).buttons(ok, btn("Back").linkTo("/")));

    Runnable replicate = () -> REST.get("http://localhost:8888/cfg", Object.class, U::print);

    Jobs.scheduleAtFixedRate(replicate, 3, 3, TimeUnit.SECONDS);
  }
Пример #9
0
  private long _insert(Object record, boolean failOnReadOnlyTx) {
    U.notNull(record, "record");
    secureInsert(record);

    sharedLock();
    try {
      if (failOnReadOnlyTx) {
        failIfReadonlyTx();
      }

      long id = data.ids.incrementAndGet();
      Beany.setId(record, id);

      // Optimistic concurrency control through the "version" property
      if (Beany.hasProperty(record, VERSION)) {
        // FIXME rollback version in TX fails
        Beany.setPropValue(record, VERSION, 1);
      }

      Date now = new Date();

      if (Beany.hasProperty(record, CREATED_BY)) {
        Beany.setPropValue(record, CREATED_BY, username());
      }

      if (Beany.hasProperty(record, CREATED_ON)) {
        Beany.setPropValue(record, CREATED_ON, now);
      }

      if (Beany.hasProperty(record, LAST_UPDATED_BY)) {
        Beany.setPropValue(record, LAST_UPDATED_BY, username());
      }

      if (Beany.hasProperty(record, LAST_UPDATED_ON)) {
        Beany.setPropValue(record, LAST_UPDATED_ON, now);
      }

      if (data.insideTx.get()) {
        if (data.txInsertions.putIfAbsent(id, INSERTION) != null) {
          throw new IllegalStateException("Cannot insert changelog record with existing ID: " + id);
        }
      }

      if (data.data.putIfAbsent(id, rec(record)) != null) {
        throw new IllegalStateException("Cannot insert record with existing ID: " + id);
      }

      updateChangesFromRels(record);

      data.lastChangedOn.set(System.currentTimeMillis());

      Log.audit("Inserted DB record", "id", id);
      return id;
    } finally {
      sharedUnlock();
    }
  }
Пример #10
0
 private void invokeCallbacks(Callback<Void>[] callbacks, Throwable e) {
   for (Callback<Void> callback : callbacks) {
     try {
       callback.onDone(null, e);
     } catch (Throwable e2) {
       Log.error("Transaction callback error", e2);
     }
   }
 }
Пример #11
0
  private void persist() {
    while (!Thread.interrupted()) {
      try {
        persistData();
      } catch (Exception e1) {
        Log.error("Failed to persist data!", e1);
      }

      if (data.active.get()) {
        try {
          Thread.sleep(100);
        } catch (InterruptedException e) {
        }
      } else {
        try {
          persistData();
        } catch (Exception e1) {
          Log.error("Failed to persist data!", e1);
        }
        return;
      }
    }
  }
Пример #12
0
  /**
   * ACID transactional semantics:<br>
   * - Atomicity with automatic rollback in case of exception,<br>
   * - Consistency - only with constraints enforced programmatically inside transaction,<br>
   * - Isolation is serializable (with global lock),<br>
   * - Durability through on-commit callbacks.<br>
   */
  public void transaction(Runnable transaction, boolean readOnly, Callback<Void> txCallback) {
    globalLock();

    data.txIdCounter.set(data.ids.get());
    data.txChanges.clear();
    data.txInsertions.clear();
    data.txReadonly.set(readOnly);
    data.insideTx.set(true);

    boolean success = false;
    try {
      transaction.run();
      success = true;

    } catch (Throwable e) {
      if (SuccessException.isSuccess(e)) {
        success = true;
        throw U.rte(e);
      } else {
        Log.error("Error in transaction, rolling back", e);
        txRollback();
        if (txCallback != null) {
          txCallback.onDone(null, e);
          txCallback = null;
        }
      }

    } finally {
      data.txChanges.clear();
      data.txInsertions.clear();
      data.insideTx.set(false);

      if (persistor != null) {
        if (success && txCallback != null) {
          data.txCallbacks.add(txCallback);
        }
      } else {
        if (success && txCallback != null) {
          txCallback.onDone(null, null);
        }
      }

      globalUnlock();
    }
  }
Пример #13
0
 public void invalidateCache() {
   Log.debug("Invalidating Mustache cache");
   mustacheCache.invalidateAll();
   lambdaCache.invalidateAll();
 }
Пример #14
0
  private void update_(long id, Object record, boolean reflectRelChanges, boolean checkSecurity) {

    failIfReadonlyTx();
    validateId(id);

    Rec old = data.data.get(id);
    Object entity = obj(old);

    if (checkSecurity) {
      secureUpdate(entity);
    }

    // Optimistic concurrency control through the "version" property
    Long oldVersion = U.or(Beany.getPropValueOfType(entity, VERSION, Long.class, null), 0L);
    Long recordVersion = U.or(Beany.getPropValueOfType(record, VERSION, Long.class, null), 0L);

    occErrorIf(
        !U.eq(oldVersion, recordVersion),
        "Concurrent modification occured while updating the object with ID=%s!",
        id);

    Beany.setId(record, id);

    if (!sudo && checkSecurity) {
      boolean canUpdate = false;
      for (Prop prop : Beany.propertiesOf(record)) {
        if (!Secure.getPropertyPermissions(username(), entity.getClass(), entity, prop.getName())
            .change) {
          prop.set(record, prop.get(entity));
        } else {
          canUpdate = true;
        }
      }
      U.secure(
          canUpdate,
          "Not enough privileges to update any column of %s!",
          entity.getClass().getSimpleName());
    }

    // Optimistic concurrency control through the "version" property
    if (Beany.hasProperty(record, VERSION)) {
      Beany.setPropValue(record, VERSION, oldVersion + 1);
    }

    if (checkSecurity) {
      secureUpdate(record);
    }

    if (Beany.hasProperty(record, LAST_UPDATED_BY)) {
      Beany.setPropValue(record, LAST_UPDATED_BY, username());
    }

    if (Beany.hasProperty(record, LAST_UPDATED_ON)) {
      Beany.setPropValue(record, LAST_UPDATED_ON, new Date());
    }

    boolean updated = data.data.replace(id, old, rec(record));

    occErrorIf(
        !updated, "Concurrent modification occured while updating the object with ID=%s!", id);

    if (data.insideTx.get()) {
      data.txChanges.putIfAbsent(id, old);
    }

    if (old == null) {
      throw new IllegalStateException("Cannot update non-existing record with ID=" + id);
    }

    if (reflectRelChanges) {
      updateChangesFromRels(record);
    }

    data.lastChangedOn.set(System.currentTimeMillis());

    Log.audit("Updated DB record", "id", id);
  }
Пример #15
0
  public void loadFrom(InputStream in) {
    globalLock();

    try {
      data.data.clear();

      BufferedReader reader = new BufferedReader(new InputStreamReader(in));

      String line = reader.readLine();
      byte[] bytes = line.getBytes();

      U.must(line != null, "Missing meta-data at the first line in the database file!");

      Map<String, Object> meta = U.map();
      data.serializer.deserialize(bytes, meta);

      Log.info(
          "Database meta-data",
          META_TIMESTAMP,
          meta.get(META_TIMESTAMP),
          META_UPTIME,
          meta.get(META_UPTIME));

      while ((line = reader.readLine()) != null) {
        bytes = line.getBytes();
        Map<String, Object> map = U.map();
        data.serializer.deserialize(bytes, map);

        Object idObj = map.get(ID);
        U.must(idObj != null, "Found DB record without ID: %s", line);

        long id = Cls.convert(idObj, Long.class);
        String className = ((String) map.get("_class"));

        String[] nameParts = className.split("\\.");
        String simpleName = nameParts[nameParts.length - 1];
        List<Class<?>> classes = Scan.byName(simpleName, null, null);

        if (classes.size() == 1) {
          Class<?> type = classes.get(0);
          data.data.put(id, new Rec(type, bytes));

          if (id > data.ids.get()) {
            data.ids.set(id);
          }

        } else {
          if (classes.isEmpty()) {
            Log.error("Cannot find the class of a DB record!", "id", id, "class", className);
          } else {
            Log.error("Found more than 1 class of a DB record!", "id", id, "class", className);
          }
        }
      }

      data.prevData = new ConcurrentSkipListMap<Long, Rec>(data.data);

      reader.close();

    } catch (IOException e) {
      throw new RuntimeException("Cannot load database!", e);
    } finally {
      globalUnlock();
    }
  }
Пример #16
-1
  private Mustache compileIfChanged(String filename, boolean partial) {
    Mustache template = mustacheCache.getIfPresent(filename);

    if (template == null) {

      String desc = partial ? "partial" : "template";
      Log.debug("Compiling Mustache " + desc, "name", filename);

      Res res = getResource(filename, partial);
      template = customCompile(filename, res);

      res.onChange(
              "mustache",
              new Runnable() {
                @Override
                public void run() {
                  invalidateCache();
                }
              })
          .trackChanges();

      mustacheCache.put(filename, template);
    }

    return template;
  }