Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Таразанов Максим, ИТМО, М4139 #27

Closed
wants to merge 11 commits into from
Closed
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
build

.idea
.obsidian


# Compiled source #
###################
Expand Down
14 changes: 14 additions & 0 deletions src/main/java/ru/vk/itmo/test/maximtarazanov/MyFactory.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package ru.vk.itmo.test.maximtarazanov;

import ru.vk.itmo.Service;
import ru.vk.itmo.ServiceConfig;
import ru.vk.itmo.test.ServiceFactory;

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

@Override
public Service create(ServiceConfig config) {
return new MyService(config);
}
}
129 changes: 129 additions & 0 deletions src/main/java/ru/vk/itmo/test/maximtarazanov/MyServer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
package ru.vk.itmo.test.maximtarazanov;

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.Config;
import ru.vk.itmo.dao.Entry;
import ru.vk.itmo.test.maximtarazanov.dao.ReferenceDao;

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

public class MyServer extends HttpServer {

private final ReferenceDao dao;
private static final long FLUSH_THRESHOLD_BYTES = 1 << 20;
private static final String PATH = "/v0/entity";

public MyServer(ServiceConfig config) throws IOException {
super(createServerConfig(config));
dao = new ReferenceDao(new Config(config.workingDir(), FLUSH_THRESHOLD_BYTES));
}

private static HttpServerConfig createServerConfig(ServiceConfig serviceConfig) {
HttpServerConfig httpServerConfig = new HttpServerConfig();
AcceptorConfig acceptorConfig = new AcceptorConfig();

acceptorConfig.port = serviceConfig.selfPort();
acceptorConfig.reusePort = true;

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

return httpServerConfig;
}

private static MemorySegment toMemorySegment(String string) {
return MemorySegment.ofArray(string.getBytes(StandardCharsets.UTF_8));
}

public void close() {
try {
dao.close();
} catch (IOException e) {
throw new RuntimeException(e);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Define and throw a dedicated exception instead of using a generic one.

}
}

@Path(PATH)
@RequestMethod(Request.METHOD_GET)
public final Response get(@Param(value = "id", required = true) String id) {
MemorySegment key =
(id == null || id.isEmpty())
? null
: toMemorySegment(id);

if (key == null) {
return new Response(Response.BAD_REQUEST, Response.EMPTY);
}

Entry<MemorySegment> entry = dao.get(key);

if (entry == null) {
return new Response(Response.NOT_FOUND, Response.EMPTY);
}

return Response.ok(entry.value().toArray(ValueLayout.JAVA_BYTE));
}

@Path(PATH)
@RequestMethod(Request.METHOD_PUT)
public final Response put(@Param(value = "id", required = true) String id, Request request) {
MemorySegment key =
(id == null || id.isEmpty())
? null
: toMemorySegment(id);

if (key == null) {
return new Response(Response.BAD_REQUEST, Response.EMPTY);
}

Entry<MemorySegment> entry = new BaseEntry<>(
key,
MemorySegment.ofArray(request.getBody())
);

dao.upsert(entry);

return new Response(Response.CREATED, Response.EMPTY);
}

@Path(PATH)
@RequestMethod(Request.METHOD_DELETE)
public final Response delete(@Param(value = "id", required = true) String id) {
MemorySegment key =
(id == null || id.isEmpty())
? null
: toMemorySegment(id);

if (key == null) {
return new Response(Response.BAD_REQUEST, Response.EMPTY);
}

dao.upsert(new BaseEntry<>(key, null));

return new Response(Response.ACCEPTED, Response.EMPTY);
}

@Path(PATH)
public Response otherMethod() {
return new Response(Response.METHOD_NOT_ALLOWED, Response.EMPTY);
}

@Override
public void handleDefault(Request request, HttpSession session) throws IOException {
Response response = new Response(Response.BAD_REQUEST, Response.EMPTY);
session.sendResponse(response);
}
}
31 changes: 31 additions & 0 deletions src/main/java/ru/vk/itmo/test/maximtarazanov/MyService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package ru.vk.itmo.test.maximtarazanov;

import ru.vk.itmo.Service;
import ru.vk.itmo.ServiceConfig;

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

public class MyService implements Service {
private MyServer server;

private ServiceConfig config;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Private field 'config' could be made final; it is only initialized in the declaration or constructor.


public MyService(ServiceConfig config) {
this.config = config;
}

@Override
public CompletableFuture<Void> start() throws IOException {
server = new MyServer(config);
server.start();
return CompletableFuture.completedFuture(null);
}

@Override
public CompletableFuture<Void> stop() throws IOException {
server.stop();
server.close();
return CompletableFuture.completedFuture(null);
}
}
22 changes: 22 additions & 0 deletions src/main/java/ru/vk/itmo/test/maximtarazanov/ServerMain.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package ru.vk.itmo.test.maximtarazanov;

import ru.vk.itmo.ServiceConfig;

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

public class ServerMain {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All methods are static. Consider using a utility class instead. Alternatively, you could add a private constructor or make the class abstract to silence this warning.

public static void main(String[] args) throws IOException {
MyServer server = new MyServer(
new ServiceConfig(
8080,
"http://localhost",
List.of("http://localhost"),
Files.createTempDirectory("dao")
)
);

server.start();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package ru.vk.itmo.test.maximtarazanov.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.maximtarazanov.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/maximtarazanov/dao/MemTable.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package ru.vk.itmo.test.maximtarazanov.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
Loading