-
Notifications
You must be signed in to change notification settings - Fork 48
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
7b048ca
commit 1735fc0
Showing
4 changed files
with
228 additions
and
0 deletions.
There are no files selected for viewing
9 changes: 9 additions & 0 deletions
9
src/main/java/ru/vk/itmo/test/smirnovdmitrii/server/DaoHttpServerConfig.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package ru.vk.itmo.test.smirnovdmitrii.server; | ||
|
||
import one.nio.http.HttpServerConfig; | ||
|
||
import java.nio.file.Path; | ||
|
||
public class DaoHttpServerConfig extends HttpServerConfig { | ||
public Path workingDir; | ||
} |
149 changes: 149 additions & 0 deletions
149
src/main/java/ru/vk/itmo/test/smirnovdmitrii/server/Server.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,149 @@ | ||
package ru.vk.itmo.test.smirnovdmitrii.server; | ||
|
||
import one.nio.http.HttpServer; | ||
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 org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
import ru.vk.itmo.dao.BaseEntry; | ||
import ru.vk.itmo.dao.Config; | ||
import ru.vk.itmo.dao.Dao; | ||
import ru.vk.itmo.dao.Entry; | ||
import ru.vk.itmo.test.smirnovdmitrii.dao.DaoImpl; | ||
|
||
import java.io.IOException; | ||
import java.io.UncheckedIOException; | ||
import java.lang.foreign.MemorySegment; | ||
import java.lang.foreign.ValueLayout; | ||
import java.nio.charset.StandardCharsets; | ||
import java.util.function.Function; | ||
|
||
import static one.nio.http.Request.METHOD_DELETE; | ||
import static one.nio.http.Request.METHOD_GET; | ||
import static one.nio.http.Request.METHOD_PUT; | ||
|
||
public class Server extends HttpServer { | ||
private static final int DEFAULT_FLUSH_VALUE_BYTES = 16 * 1024; // 16 kb | ||
private static final String REQUEST_PATH = "/v0/entity"; | ||
private static final Response INVALID_ID_RESPONSE = new Response( | ||
Response.BAD_REQUEST, | ||
"invalid id".getBytes(StandardCharsets.UTF_8) | ||
); | ||
private static final Response INVALID_REQUEST_RESPONSE = new Response( | ||
Response.BAD_REQUEST, | ||
new byte[0] | ||
); | ||
private static final Response INVALID_METHOD_RESPONSE = new Response( | ||
Response.METHOD_NOT_ALLOWED, | ||
new byte[0] | ||
); | ||
private static final Response CREATED_RESPONSE = new Response( | ||
Response.CREATED, | ||
new byte[0] | ||
); | ||
private static final Response ACCEPTED_RESPONSE = new Response( | ||
Response.ACCEPTED, | ||
new byte[0] | ||
); | ||
private static final Logger logger = LoggerFactory.getLogger(Server.class); | ||
|
||
private final DaoHttpServerConfig config; | ||
private Dao<MemorySegment, Entry<MemorySegment>> dao; | ||
|
||
public Server( | ||
final DaoHttpServerConfig config | ||
) throws IOException { | ||
super(config); | ||
this.config = config; | ||
startDao(); | ||
} | ||
|
||
private void startDao() { | ||
final Config daoConfig = new Config(config.workingDir, DEFAULT_FLUSH_VALUE_BYTES); | ||
this.dao = new DaoImpl(daoConfig); | ||
} | ||
|
||
@Path(REQUEST_PATH) | ||
@RequestMethod(METHOD_GET) | ||
public Response get( | ||
@Param("id") final String id | ||
) { | ||
return handleEntityRequest(id, key -> { | ||
final Entry<MemorySegment> entry = dao.get(key); | ||
if (entry == null) { | ||
return new Response(Response.NOT_FOUND, new byte[0]); | ||
} | ||
return Response.ok(entry.value().toArray(ValueLayout.JAVA_BYTE)); | ||
}); | ||
} | ||
|
||
@Path(REQUEST_PATH) | ||
@RequestMethod(METHOD_PUT) | ||
public Response put( | ||
@Param("id") final String id, | ||
final Request request | ||
) { | ||
return handleEntityRequest(id, key -> { | ||
final MemorySegment value = MemorySegment.ofArray(request.getBody()); | ||
final Entry<MemorySegment> entry = new BaseEntry<>(key, value); | ||
dao.upsert(entry); | ||
return CREATED_RESPONSE; | ||
}); | ||
} | ||
|
||
@Path(REQUEST_PATH) | ||
@RequestMethod(METHOD_DELETE) | ||
public Response delete( | ||
@Param("id") final String id | ||
) { | ||
return handleEntityRequest(id, key -> { | ||
final Entry<MemorySegment> entry = new BaseEntry<>(key, null); | ||
dao.upsert(entry); | ||
return ACCEPTED_RESPONSE; | ||
}); | ||
} | ||
|
||
private Response handleEntityRequest( | ||
final String id, | ||
final Function<MemorySegment, Response> keyToResponse | ||
) { | ||
if (isInvalidKey(id)) { | ||
return INVALID_ID_RESPONSE; | ||
} | ||
final MemorySegment key = MemorySegment.ofArray(id.getBytes(StandardCharsets.UTF_8)); | ||
try { | ||
return keyToResponse.apply(key); | ||
} catch (final Exception e) { | ||
logger.error(e.getMessage(), e); | ||
return new Response(Response.INTERNAL_ERROR, e.getMessage().getBytes(StandardCharsets.UTF_8)); | ||
} | ||
} | ||
|
||
@Override | ||
public void handleDefault(Request request, HttpSession session) throws IOException { | ||
final int method = request.getMethod(); | ||
if (method == METHOD_GET || method == METHOD_PUT || method == METHOD_DELETE) { | ||
session.sendResponse(INVALID_REQUEST_RESPONSE); | ||
} else { | ||
session.sendResponse(INVALID_METHOD_RESPONSE); | ||
} | ||
} | ||
|
||
public boolean isInvalidKey(final String key) { | ||
return key == null || key.isEmpty(); | ||
} | ||
|
||
@Override | ||
public synchronized void stop() { | ||
super.stop(); | ||
try { | ||
dao.close(); | ||
} catch (final IOException e) { | ||
throw new UncheckedIOException(e); | ||
} | ||
} | ||
} |
20 changes: 20 additions & 0 deletions
20
src/main/java/ru/vk/itmo/test/smirnovdmitrii/server/ServiceFactoryImpl.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
package ru.vk.itmo.test.smirnovdmitrii.server; | ||
|
||
import ru.vk.itmo.Service; | ||
import ru.vk.itmo.ServiceConfig; | ||
import ru.vk.itmo.test.ServiceFactory; | ||
|
||
import java.io.IOException; | ||
import java.io.UncheckedIOException; | ||
|
||
@ServiceFactory(stage = 1) | ||
public class ServiceFactoryImpl implements ServiceFactory.Factory { | ||
@Override | ||
public Service create(ServiceConfig config) { | ||
try { | ||
return new ServiceImpl(config); | ||
} catch (final IOException e) { | ||
throw new UncheckedIOException(e); | ||
} | ||
} | ||
} |
50 changes: 50 additions & 0 deletions
50
src/main/java/ru/vk/itmo/test/smirnovdmitrii/server/ServiceImpl.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
package ru.vk.itmo.test.smirnovdmitrii.server; | ||
|
||
import one.nio.http.HttpServer; | ||
import one.nio.server.AcceptorConfig; | ||
import ru.vk.itmo.Service; | ||
import ru.vk.itmo.ServiceConfig; | ||
|
||
import java.io.IOException; | ||
import java.io.UncheckedIOException; | ||
import java.util.concurrent.CompletableFuture; | ||
|
||
public class ServiceImpl implements Service { | ||
|
||
private final ServiceConfig config; | ||
private HttpServer server; | ||
|
||
private static final String ADDRESS = "localhost"; | ||
|
||
public ServiceImpl(final ServiceConfig config) throws IOException { | ||
this.config = config; | ||
} | ||
|
||
private static DaoHttpServerConfig createDaoHttpServerConfig(final ServiceConfig config) { | ||
final DaoHttpServerConfig serverConfig = new DaoHttpServerConfig(); | ||
final AcceptorConfig acceptorConfig = new AcceptorConfig(); | ||
acceptorConfig.address = ADDRESS; | ||
acceptorConfig.port = config.selfPort(); | ||
acceptorConfig.reusePort = true; | ||
|
||
serverConfig.acceptors = new AcceptorConfig[] {acceptorConfig}; | ||
serverConfig.closeSessions = true; | ||
serverConfig.workingDir = config.workingDir(); | ||
|
||
return serverConfig; | ||
} | ||
@Override | ||
public CompletableFuture<Void> start() { | ||
try { | ||
server = new Server(createDaoHttpServerConfig(config)); | ||
} catch (final IOException e) { | ||
throw new UncheckedIOException(e); | ||
} | ||
return CompletableFuture.runAsync(server::start); | ||
} | ||
|
||
@Override | ||
public CompletableFuture<Void> stop() { | ||
return CompletableFuture.runAsync(server::stop); | ||
} | ||
} |