/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
* Copyright (c) 2014, MPL CodeInside http://codeinside.ru
*/
package ru.codeinside.gses.form.docx;
import org.codehaus.jackson.map.ObjectMapper;
import ru.codeinside.gses.form.FormConverter;
import ru.codeinside.gses.form.FormData;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.logging.Level;
import java.util.logging.Logger;
final public class ConverterProxy implements FormConverter {
final Logger logger = Logger.getLogger(getClass().getName());
final ThreadGroup group = new ThreadGroup("docx");
final String OK = "ok" + System.getProperty("line.separator");
final ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor(new ThreadFactory() {
@Override
public Thread newThread(Runnable runnable) {
Thread thread = new Thread(group, runnable, "docxProxy", 256 * 1024);
thread.setDaemon(true);
thread.setPriority(Thread.NORM_PRIORITY - 1);
return thread;
}
});
RemoteService remoteService;
public ConverterProxy() {
executorService.scheduleWithFixedDelay(new Runnable() {
@Override
public void run() {
ping();
}
}, 30, 30, TimeUnit.SECONDS);
executorService.schedule(new Runnable() {
@Override
public void run() {
refreshService();
}
}, 5, TimeUnit.SECONDS);
}
public void close() {
closeService();
executorService.shutdownNow();
}
@Override
public void createForm(FormData data) {
if (data == null) {
return;
}
final byte[] request = createRequest(data);
Future<?> future = executorService.submit(new Runnable() {
@Override
public void run() {
process(request);
}
});
try {
future.get(30, TimeUnit.SECONDS);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException("interrupted");
} catch (ExecutionException e) {
throw new RuntimeException(e.getCause());
} catch (TimeoutException e) {
throw new RuntimeException("executionTimeOut");
}
}
void process(byte[] request) {
refreshService();
long startAt = System.currentTimeMillis();
StringBuilder response = new StringBuilder();
try {
OutputStream out = remoteService.getOut();
BufferedReader in = remoteService.getIn();
out.write(request);
out.write('\n');
out.flush();
for (; ; ) {
if (!in.ready()) {
long now = System.currentTimeMillis();
if (now - startAt > 25000L) {
closeService();
throw new RuntimeException("responseTimeOut");
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException("interrupted");
}
continue;
}
int c = in.read();
if (c == -1) {
closeService();
throw new RuntimeException("brokenPipe");
}
response.append((char) c);
if (OK.equals(response.toString())) {
break;
}
}
} catch (IOException e) {
closeService();
throw new RuntimeException(e);
}
}
byte[] createRequest(FormData data) {
ObjectMapper objectMapper = new ObjectMapper();
ByteArrayOutputStream bos = new ByteArrayOutputStream();
try {
objectMapper.writeValue(bos, data);
} catch (IOException e) {
throw new RuntimeException(e);
}
return bos.toByteArray();
}
void refreshService() {
if (remoteService != null) {
if (!remoteService.isAlive()) {
closeService();
}
}
if (remoteService == null) {
logger.fine("open service");
remoteService = new RemoteService();
}
}
void closeService() {
if (remoteService != null) {
logger.fine("close service");
remoteService.close();
remoteService = null;
}
}
void ping() {
refreshService();
OutputStream out = remoteService.getOut();
try {
out.write('\n');
out.flush();
} catch (IOException e) {
logger.log(Level.INFO, "io error", e);
closeService();
}
}
}