Skip to content

Commit

Permalink
base implementation of server
Browse files Browse the repository at this point in the history
  • Loading branch information
yulalenk committed Feb 18, 2024
1 parent e22da0c commit a4bd3a0
Show file tree
Hide file tree
Showing 14 changed files with 1,579 additions and 0 deletions.
103 changes: 103 additions & 0 deletions src/main/java/ru/vk/itmo/test/alenkovayulya/ServerImpl.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package ru.vk.itmo.test.alenkovayulya;

import one.nio.http.HttpServer;
import one.nio.http.HttpServerConfig;
import one.nio.http.HttpSession;
import one.nio.http.Param;
import one.nio.http.Path;
import one.nio.http.Request;
import one.nio.http.RequestMethod;
import one.nio.http.Response;
import one.nio.server.AcceptorConfig;
import ru.vk.itmo.ServiceConfig;
import ru.vk.itmo.dao.BaseEntry;
import ru.vk.itmo.dao.Entry;
import ru.vk.itmo.test.alenkovayulya.dao.ReferenceDao;

import java.io.IOException;
import java.io.UncheckedIOException;
import java.lang.foreign.MemorySegment;
import java.lang.foreign.ValueLayout;
import java.nio.charset.StandardCharsets;

public class ServerImpl extends HttpServer {

private final ReferenceDao referenceDao;

public ServerImpl(ServiceConfig serviceConfig, ReferenceDao referenceDao) throws IOException {
super(createServerConfig(serviceConfig));
this.referenceDao = referenceDao;
}

private static HttpServerConfig createServerConfig(ServiceConfig serviceConfig) {
HttpServerConfig serverConfig = new HttpServerConfig();
AcceptorConfig acceptorConfig = new AcceptorConfig();
acceptorConfig.port = serviceConfig.selfPort();
acceptorConfig.reusePort = true;

serverConfig.acceptors = new AcceptorConfig[]{acceptorConfig};
serverConfig.closeSessions = true;
return serverConfig;
}

@Path("/v0/entity")
@RequestMethod(Request.METHOD_GET)
public Response getEntity(@Param(value = "id", required = true) String id) {
if (isEmptyId(id)) {
return new Response(Response.BAD_REQUEST, Response.EMPTY);
}
Entry<MemorySegment> value = referenceDao.get(convertBytesToMemorySegment(id.getBytes(StandardCharsets.UTF_8)));

return value == null ? new Response(Response.NOT_FOUND, Response.EMPTY)
: Response.ok(value.value().toArray(ValueLayout.JAVA_BYTE));
}

@Path("/v0/entity")
@RequestMethod(Request.METHOD_PUT)
public Response putEntity(@Param(value = "id", required = true) String id, Request request) {
if (isEmptyId(id)) {
return new Response(Response.BAD_REQUEST, Response.EMPTY);
}
referenceDao.upsert(new BaseEntry<>(
convertBytesToMemorySegment(id.getBytes(StandardCharsets.UTF_8)),
convertBytesToMemorySegment(request.getBody())));
return new Response(Response.CREATED, Response.EMPTY);
}

@Path("/v0/entity")
@RequestMethod(Request.METHOD_DELETE)
public Response deleteEntity(@Param(value = "id", required = true) String id) {
if (isEmptyId(id)) {
return new Response(Response.BAD_REQUEST, Response.EMPTY);
}
referenceDao.upsert(new BaseEntry<>(
convertBytesToMemorySegment(id.getBytes(StandardCharsets.UTF_8)), null));
return new Response(Response.ACCEPTED, Response.EMPTY);
}

@Override
public void handleDefault(Request request, HttpSession session) throws IOException {
switch (request.getMethodName()) {
case "GET", "PUT", "DELETE" -> session.sendResponse(new Response(Response.BAD_REQUEST, Response.EMPTY));
default -> session.sendResponse(new Response(Response.METHOD_NOT_ALLOWED, Response.EMPTY));
}
}

@Override
public synchronized void stop() {
super.stop();
try {
referenceDao.close();
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}

private boolean isEmptyId(String id) {
return id.isEmpty() && id.isBlank();
}

private MemorySegment convertBytesToMemorySegment(byte[] byteArray) {
return MemorySegment.ofArray(byteArray);
}
}
26 changes: 26 additions & 0 deletions src/main/java/ru/vk/itmo/test/alenkovayulya/ServerInitializer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package ru.vk.itmo.test.alenkovayulya;

import ru.vk.itmo.ServiceConfig;
import ru.vk.itmo.dao.Config;
import ru.vk.itmo.test.alenkovayulya.dao.ReferenceDao;

import java.io.IOException;
import java.nio.file.Files;
import java.util.List;

public class ServerInitializer {
public static final int PORT = 8080;
public static final String URL = "http://localhost";


public static void main(String[] args) throws IOException {
ServiceConfig config = new ServiceConfig(PORT, URL, List.of(URL),
Files.createTempDirectory("reports")
);

ReferenceDao dao = new ReferenceDao(new Config(config.workingDir(), 1024));
ServerImpl server = new ServerImpl(config, dao);
server.start();
}

}
44 changes: 44 additions & 0 deletions src/main/java/ru/vk/itmo/test/alenkovayulya/ServiceImpl.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package ru.vk.itmo.test.alenkovayulya;

import ru.vk.itmo.Service;
import ru.vk.itmo.ServiceConfig;
import ru.vk.itmo.dao.Config;
import ru.vk.itmo.test.ServiceFactory;
import ru.vk.itmo.test.alenkovayulya.dao.ReferenceDao;

import java.io.IOException;
import java.util.concurrent.CompletableFuture;

public class ServiceImpl implements Service {

private ReferenceDao referenceDao;
private ServerImpl server;
private final ServiceConfig config;

public ServiceImpl(ServiceConfig config) {
this.config = config;

}
@Override
public CompletableFuture<Void> start() throws IOException {
referenceDao = new ReferenceDao(new Config(config.workingDir(), 1024));
server = new ServerImpl(config, referenceDao);
server.start();
return CompletableFuture.completedFuture(null);
}
@Override
public CompletableFuture<Void> stop() throws IOException {
server.stop();
referenceDao.close();
return CompletableFuture.completedFuture(null);
}

@ServiceFactory(stage = 1)
public static class Factory implements ServiceFactory.Factory {

@Override
public Service create(ServiceConfig config) {
return new ServiceImpl(config);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package ru.vk.itmo.test.alenkovayulya.dao;

import java.io.IOException;
import java.lang.foreign.MemorySegment;
import java.nio.ByteBuffer;

/**
* Growable buffer with {@link ByteBuffer} and {@link MemorySegment} interface.
*
* @author incubos
*/
final class ByteArraySegment {
private byte[] array;
private MemorySegment segment;

ByteArraySegment(final int capacity) {
this.array = new byte[capacity];
this.segment = MemorySegment.ofArray(array);
}

void withArray(final ArrayConsumer consumer) throws IOException {
consumer.process(array);
}

MemorySegment segment() {
return segment;
}

void ensureCapacity(final long size) {
if (size > Integer.MAX_VALUE) {
throw new IllegalArgumentException("Too big!");
}

final int capacity = (int) size;
if (array.length >= capacity) {
return;
}

// Grow to the nearest bigger power of 2
final int newSize = Integer.highestOneBit(capacity) << 1;
array = new byte[newSize];
segment = MemorySegment.ofArray(array);
}

interface ArrayConsumer {
void process(byte[] array) throws IOException;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package ru.vk.itmo.test.alenkovayulya.dao;

import ru.vk.itmo.dao.Entry;

import java.lang.foreign.MemorySegment;
import java.util.Iterator;
import java.util.NoSuchElementException;

/**
* Filters non tombstone {@link Entry}s.
*
* @author incubos
*/
final class LiveFilteringIterator implements Iterator<Entry<MemorySegment>> {
private final Iterator<Entry<MemorySegment>> delegate;
private Entry<MemorySegment> next;

LiveFilteringIterator(final Iterator<Entry<MemorySegment>> delegate) {
this.delegate = delegate;
skipTombstones();
}

private void skipTombstones() {
while (delegate.hasNext()) {
final Entry<MemorySegment> entry = delegate.next();
if (entry.value() != null) {
this.next = entry;
break;
}
}
}

@Override
public boolean hasNext() {
return next != null;
}

@Override
public Entry<MemorySegment> next() {
if (!hasNext()) {
throw new NoSuchElementException();
}

// Consume
final Entry<MemorySegment> result = next;
next = null;

skipTombstones();

return result;
}
}
49 changes: 49 additions & 0 deletions src/main/java/ru/vk/itmo/test/alenkovayulya/dao/MemTable.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package ru.vk.itmo.test.alenkovayulya.dao;

import ru.vk.itmo.dao.Entry;

import java.lang.foreign.MemorySegment;
import java.util.Iterator;
import java.util.NavigableMap;
import java.util.concurrent.ConcurrentSkipListMap;

/**
* Memory table.
*
* @author incubos
*/
final class MemTable {
private final NavigableMap<MemorySegment, Entry<MemorySegment>> map =
new ConcurrentSkipListMap<>(
MemorySegmentComparator.INSTANCE);

boolean isEmpty() {
return map.isEmpty();
}

Iterator<Entry<MemorySegment>> get(
final MemorySegment from,
final MemorySegment to) {
if (from == null && to == null) {
// All
return map.values().iterator();
} else if (from == null) {
// Head
return map.headMap(to).values().iterator();
} else if (to == null) {
// Tail
return map.tailMap(from).values().iterator();
} else {
// Slice
return map.subMap(from, to).values().iterator();
}
}

Entry<MemorySegment> get(final MemorySegment key) {
return map.get(key);
}

Entry<MemorySegment> upsert(final Entry<MemorySegment> entry) {
return map.put(entry.key(), entry);
}
}
Loading

0 comments on commit a4bd3a0

Please sign in to comment.