/* * Copyright (C) 2010-2014 Laurent CLOUET * Author Laurent CLOUET * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; version 2 * of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ package com.sheepit.client; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.PrintWriter; import java.io.StringWriter; import java.io.UnsupportedEncodingException; import java.net.*; import java.time.Duration; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Objects; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import com.sheepit.client.datamodel.SpeedTestTarget; import com.sheepit.client.datamodel.SpeedTestResult; import com.sheepit.client.datamodel.SpeedTestTargetResult; import com.sheepit.client.hardware.hwid.HWIdentifier; import com.sheepit.client.os.Windows; import lombok.Getter; import org.simpleframework.xml.core.Persister; import okhttp3.Call; import okhttp3.FormBody; import okhttp3.HttpUrl; import okhttp3.MediaType; import okhttp3.MultipartBody; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.RequestBody; import okhttp3.Response; import okhttp3.JavaNetCookieJar; import com.sheepit.client.hardware.cpu.CPU; import com.sheepit.client.Configuration.ComputeType; import com.sheepit.client.Error.ServerCode; import com.sheepit.client.datamodel.CacheFileMD5; import com.sheepit.client.datamodel.FileMD5; import com.sheepit.client.datamodel.HeartBeatInfos; import com.sheepit.client.datamodel.JobInfos; import com.sheepit.client.datamodel.JobValidation; import com.sheepit.client.datamodel.RequestEndPoint; import com.sheepit.client.datamodel.ServerConfig; import com.sheepit.client.exception.FermeException; import com.sheepit.client.exception.FermeExceptionBadResponseFromServer; import com.sheepit.client.exception.FermeExceptionNoRendererAvailable; import com.sheepit.client.exception.FermeExceptionNoRightToRender; import com.sheepit.client.exception.FermeExceptionNoSession; import com.sheepit.client.exception.FermeExceptionNoSpaceLeftOnDevice; import com.sheepit.client.exception.FermeExceptionPathInvalid; import com.sheepit.client.exception.FermeExceptionNoWritePermission; import com.sheepit.client.exception.FermeExceptionServerInMaintenance; import com.sheepit.client.exception.FermeExceptionServerOverloaded; import com.sheepit.client.exception.FermeExceptionSessionDisabled; import com.sheepit.client.exception.FermeServerDown; import com.sheepit.client.os.OS; public class Server extends Thread { private static final int NUMBER_OF_SPEEDTEST_RESULTS = 3; final private String HTTP_USER_AGENT = "Java/" + System.getProperty("java.version"); private String base_url; private final OkHttpClient httpClient; @Getter private ServerConfig serverConfig; private Configuration user_config; private Client client; private Log log; private long lastRequestTime; private int keepmealive_duration; // time in ms private TransferStats dlStats = new TransferStats(); private TransferStats ulStats = new TransferStats(); public Server(String url_, Configuration user_config_, Client client_) { super(); this.base_url = url_; this.user_config = user_config_; this.client = client_; this.log = Log.getInstance(this.user_config); this.lastRequestTime = 0; this.keepmealive_duration = 15 * 60 * 1000; // default 15min // OkHttp performs best when we create a single OkHttpClient instance and reuse it for all of the HTTP calls. This is because each client holds its own // connection pool and thread pools.Reusing connections and threads reduces latency and saves memory. Conversely, creating a client for each request // wastes resources on idle pools. this.httpClient = getOkHttpClient(); } public void run() { this.stayAlive(); } public void stayAlive() { while (true) { long current_time = new Date().getTime(); if ((current_time - this.lastRequestTime) > this.keepmealive_duration) { try { HttpUrl.Builder urlBuilder = Objects.requireNonNull(HttpUrl.parse(this.getPage("keepmealive"))).newBuilder(); if (this.client != null && this.client.getRenderingJob() != null) { Job job = this.client.getRenderingJob(); urlBuilder.addQueryParameter("frame", job.getFrameNumber()).addQueryParameter("job", job.getId()); RenderProcess process = job.getProcessRender(); if (process != null) { urlBuilder.addQueryParameter("rendertime", String.valueOf(process.getDuration())) .addQueryParameter("remainingtime", String.valueOf(process.getRemainingDuration())); } } Response response = this.HTTPRequest(urlBuilder); if (response.code() == HttpURLConnection.HTTP_OK && response.body().contentType().toString().startsWith("text/xml")) { String in = response.body().string(); try { HeartBeatInfos heartBeatInfos = new Persister().read(HeartBeatInfos.class, in); ServerCode serverCode = ServerCode.fromInt(heartBeatInfos.getStatus()); if (serverCode == ServerCode.KEEPMEALIVE_STOP_RENDERING) { this.log.debug("Server::stayAlive server asked to kill local render process"); // kill the current process, it will generate an error but it's okay if (this.client != null && this.client.getRenderingJob() != null) { this.client.getRenderingJob().setServerBlockJob(true); if (this.client.getRenderingJob().getProcessRender().getProcess() != null) { this.client.getRenderingJob().setAskForRendererKill(true); OS.getOS().kill(this.client.getRenderingJob().getProcessRender().getProcess()); } } } } catch (Exception e) { // for the read this.log.debug("Server::stayAlive Exception " + e); } } } catch (NoRouteToHostException e) { this.log.debug("Server::stayAlive can not connect to server"); } catch (IOException e) { StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); e.printStackTrace(pw); this.log.debug("Server::stayAlive IOException " + e + " stacktrace: " + sw.toString()); } } try { Thread.sleep(60 * 1000); // 1min } catch (InterruptedException e) { return; } catch (Exception e) { StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); e.printStackTrace(pw); this.log.debug("Server::stayAlive Exception " + e + " stacktrace: " + sw.toString()); } } } public String toString() { return String.format("Server (base_url '%s', user_config %s", this.base_url, this.user_config); } public Error.Type getConfiguration() { OS os = OS.getOS(); String publickey = null; try { HttpUrl.Builder remoteURL = Objects.requireNonNull(HttpUrl.parse(this.base_url + "/server/config.php")).newBuilder(); FormBody formBody = new FormBody.Builder() .add("login", user_config.getLogin()) .add("password", user_config.getPassword()) .add("cpu_family", os.getCPU().family()) .add("cpu_model", os.getCPU().model()) .add("cpu_model_name", os.getCPU().name()) .add("cpu_cores", String.valueOf(user_config.getNbCores() == -1 ? os.getCPU().cores() : Math.max(CPU.MIN_CORES, user_config.getNbCores()))) .add("os", os.name()) .add("os_version", os.getVersion()) .add("ram", String.valueOf(os.getTotalMemory())) .add("bits", os.getCPU().arch()) .add("version", Configuration.jarVersion) .add("hostname", user_config.getHostname()) .add("ui", client.getGui().getClass().getSimpleName()) .add("extras", user_config.getExtras()) .add("headless", java.awt.GraphicsEnvironment.isHeadless() ? "1" : (user_config.isHeadless() ? "1" : "0")) .add("hwid", new HWIdentifier(log).getHardwareHash()) .build(); this.log.debug("Server::getConfiguration url " + remoteURL.build().toString()); Response response = this.HTTPRequest(remoteURL, formBody); int r = response.code(); String contentType = response.body().contentType().toString(); if (r == HttpURLConnection.HTTP_OK && contentType.startsWith("text/xml")) { String in = response.body().string(); serverConfig = new Persister().read(ServerConfig.class, in); if (ServerCode.fromInt(serverConfig.getStatus()) != ServerCode.OK) { return Error.ServerCodeToType(ServerCode.fromInt(serverConfig.getStatus())); } publickey = serverConfig.getPublickey(); if (publickey.isEmpty()) { publickey = null; } else { this.user_config.setPassword(publickey); } } } catch (ConnectException e) { this.log.error("Server::getConfiguration error ConnectException " + e); return Error.Type.NETWORK_ISSUE; } catch (UnknownHostException e) { this.log.error("Server::getConfiguration: exception UnknownHostException " + e); return Error.Type.NETWORK_ISSUE; } catch (UnsupportedEncodingException e) { this.log.error("Server::getConfiguration: exception UnsupportedEncodingException " + e); return Error.Type.UNKNOWN; } catch (IOException e) { this.log.error("Server::getConfiguration: exception IOException " + e); return Error.Type.UNKNOWN; } catch (Exception e) { this.log.error("Server::getConfiguration: exception Exception " + e); return Error.Type.UNKNOWN; } if (serverConfig.getSpeedTestTargets() != null && serverConfig.getSpeedTestTargets().isEmpty() == false) { try { client.getGui().status("Checking mirror connection speeds"); Speedtest speedtest = new Speedtest(log); List bestSpeedTestTargets = speedtest.doSpeedtests(serverConfig.getSpeedTestTargets().stream().map(m -> m.getUrl()).collect(Collectors.toList()), NUMBER_OF_SPEEDTEST_RESULTS); SpeedTestResult result = new SpeedTestResult(); result.setResults(bestSpeedTestTargets.stream().map(m -> { SpeedTestTargetResult targetResult = new SpeedTestTargetResult(); targetResult.setTarget(m.getUrl()); targetResult.setSpeed(m.getSpeedtest()); targetResult.setPing((int) (m.getPing().getAverage())); return targetResult; }).collect(Collectors.toList())); final Persister persister = new Persister(); try (StringWriter writer = new StringWriter()) { persister.write(result, writer); HttpUrl.Builder urlBuilder = Objects.requireNonNull(HttpUrl.parse(this.getPage("speedtest-answer"))).newBuilder(); Response response = this.HTTPRequest(urlBuilder, RequestBody.create(MediaType.parse("application/xml"), writer.toString())); if (response.code() != HttpURLConnection.HTTP_OK) { throw new IOException("Server::getConfiguration Speedtest unexpected response"); } } catch (final Exception e) { throw new IOException("Server::getConfiguration Speedtest failed to generate payload"); } } catch (IOException e) { this.log.error("Server::getConfiguration Speedtest failed: " + e); return Error.Type.NETWORK_ISSUE; } } client.setSessionStarted(true); this.client.getGui().successfulAuthenticationEvent(publickey); return Error.Type.OK; } public Job requestJob() throws FermeException { this.log.debug("Server::requestJob"); String url_contents = ""; try { OS os = OS.getOS(); long maxMemory = this.user_config.getMaxAllowedMemory(); long freeMemory = os.getFreeMemory() - 1024^2 /*One gigabyte*/ * (os instanceof Windows ? 2 : 1); //Make RAM to reserve 2GB on Windows if (maxMemory < 0) { maxMemory = freeMemory; } else if (freeMemory > 0 && maxMemory > 0) { maxMemory = Math.min(maxMemory, freeMemory); } HttpUrl.Builder urlBuilder = Objects.requireNonNull(HttpUrl.parse(this.getPage("request-job"))).newBuilder() .addQueryParameter("computemethod", String.valueOf(user_config.computeMethodToInt())) .addQueryParameter("network_dl", String.valueOf(dlStats.getRawAverageSessionSpeed())) .addQueryParameter("network_up", String.valueOf(ulStats.getRawAverageSessionSpeed())) .addQueryParameter("cpu_cores", String.valueOf(user_config.getNbCores() == -1 ? os.getCPU().cores() : (Math.max(user_config.getNbCores(), CPU.MIN_CORES)))) .addQueryParameter("ram_max", String.valueOf(maxMemory)) .addQueryParameter("rendertime_max", String.valueOf(user_config.getMaxRenderTime())); if (user_config.getComputeMethod() != ComputeType.CPU && user_config.getGPUDevice() != null) { urlBuilder.addQueryParameter("gpu_model", user_config.getGPUDevice().getModel()) .addQueryParameter("gpu_ram", String.valueOf(user_config.getGPUDevice().getMemory())) .addQueryParameter("gpu_type", user_config.getGPUDevice().getType()); } Response response = this.HTTPRequest(urlBuilder, RequestBody.create(this.generateXMLForMD5cache(), MediaType.parse("application/xml"))); int r = response.code(); String contentType = response.body().contentType().toString(); if (r == HttpURLConnection.HTTP_OK && contentType.startsWith("text/xml")) { String in = response.body().string(); JobInfos jobData = new Persister().read(JobInfos.class, in); handleFileMD5DeleteDocument(jobData.getFileMD5s()); if (jobData.getSessionStats() != null) { this.client.getGui().displayStats( new Stats(jobData.getSessionStats().getRemainingFrames(), jobData.getSessionStats().getPointsEarnedByUser(), jobData.getSessionStats().getPointsEarnedOnSession(), jobData.getSessionStats().getRenderableProjects(), jobData.getSessionStats().getWaitingProjects(), jobData.getSessionStats().getConnectedMachines())); } ServerCode serverCode = ServerCode.fromInt(jobData.getStatus()); if (serverCode != ServerCode.OK) { switch (serverCode) { case JOB_REQUEST_NOJOB: return null; case JOB_REQUEST_ERROR_NO_RENDERING_RIGHT: throw new FermeExceptionNoRightToRender(); case JOB_REQUEST_ERROR_DEAD_SESSION: throw new FermeExceptionNoSession(); case JOB_REQUEST_ERROR_SESSION_DISABLED: throw new FermeExceptionSessionDisabled(); case JOB_REQUEST_ERROR_INTERNAL_ERROR: throw new FermeExceptionBadResponseFromServer(); case JOB_REQUEST_ERROR_RENDERER_NOT_AVAILABLE: throw new FermeExceptionNoRendererAvailable(); case JOB_REQUEST_SERVER_IN_MAINTENANCE: throw new FermeExceptionServerInMaintenance(); case JOB_REQUEST_SERVER_OVERLOADED: throw new FermeExceptionServerOverloaded(); default: throw new FermeException("error requestJob: status is not ok (it's " + serverCode + ")"); } } String validationUrl = URLDecoder.decode(jobData.getRenderTask().getValidationUrl(), "UTF-8"); return new Job(this.user_config, this.client.getGui(), this.client.getLog(), jobData.getRenderTask().getId(), jobData.getRenderTask().getFrame(), jobData.getRenderTask().getPath().replace("/", File.separator), jobData.getRenderTask().getUseGpu() == 1, jobData.getRenderTask().getRendererInfos().getCommandline(), validationUrl, jobData.getRenderTask().getScript(), jobData.getRenderTask().getArchive_md5(), jobData.getRenderTask().getRendererInfos().getMd5(), jobData.getRenderTask().getName(), jobData.getRenderTask().getPassword(), jobData.getRenderTask().getSynchronous_upload().equals("1"), jobData.getRenderTask().getRendererInfos().getUpdate_method()); } else { System.out.println("Server::requestJob url " + url_contents + " r " + r + " contentType " + contentType); if (r == HttpURLConnection.HTTP_UNAVAILABLE || r == HttpURLConnection.HTTP_CLIENT_TIMEOUT) { // most likely varnish is up but apache down throw new FermeServerDown(); } else if (r == HttpURLConnection.HTTP_OK && contentType.startsWith("text/html")) { throw new FermeExceptionBadResponseFromServer(); } System.out.println(response.body().string()); } } catch (FermeException e) { throw e; } catch (NoRouteToHostException e) { throw new FermeServerDown(); } catch (UnknownHostException e) { throw new FermeServerDown(); } catch (Exception e) { StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); e.printStackTrace(pw); throw new FermeException("error requestJob: unknown exception " + e + " stacktrace: " + sw.toString()); } throw new FermeException("error requestJob, end of function"); } public Response HTTPRequest(String url) throws IOException { HttpUrl.Builder httpUrlBuilder = Objects.requireNonNull(HttpUrl.parse(url)).newBuilder(); return this.HTTPRequest(httpUrlBuilder, null); } public Response HTTPRequest(HttpUrl.Builder httpUrlBuilder) throws IOException { return this.HTTPRequest(httpUrlBuilder, null); } public Response HTTPRequest(HttpUrl.Builder httpUrlBuilder, RequestBody data_) throws IOException { String url = httpUrlBuilder.build().toString(); Request.Builder builder = new Request.Builder().addHeader("User-Agent", HTTP_USER_AGENT).url(url); this.log.debug("Server::HTTPRequest url(" + url + ")"); if (data_ != null) { builder.post(data_); } Request request = builder.build(); Response response = null; try { response = httpClient.newCall(request).execute(); if (!response.isSuccessful()) { throw new IOException("Unexpected code " + response); } this.lastRequestTime = new Date().getTime(); return response; } catch (IOException e) { throw new IOException("Unexpected response from HTTP Stack" + e.getMessage()); } } public Error.Type HTTPGetFile(String url_, String destination_, Gui gui_, String status_) throws FermeException { InputStream is = null; OutputStream output = null; try { Response response = this.HTTPRequest(url_); if (response.code() != HttpURLConnection.HTTP_OK) { this.log.error("Server::HTTPGetFile(" + url_ + ", ...) HTTP code is not " + HttpURLConnection.HTTP_OK + " it's " + response.code()); return Error.Type.DOWNLOAD_FILE; } is = response.body().byteStream(); output = new FileOutputStream(destination_ + ".partial"); long size = response.body().contentLength(); byte[] buffer = new byte[8 * 1024]; int len = 0; long written = 0; long lastUpd = 0; // last GUI progress update LocalDateTime startRequestTime = LocalDateTime.now(); while ((len = is.read(buffer)) != -1) { if (this.client.getRenderingJob().isServerBlockJob()) { return Error.Type.RENDERER_KILLED_BY_SERVER; } else if (this.client.getRenderingJob().isUserBlockJob()) { return Error.Type.RENDERER_KILLED_BY_USER; } output.write(buffer, 0, len); written += len; if ((written - lastUpd) > 1000000) { // only update the gui every 1MB if (size != -1) { // no header for contentlength gui_.status(status_, (int) (100.0 * written / size), written); } lastUpd = written; } } LocalDateTime endRequestTime = LocalDateTime.now(); Duration duration = Duration.between(startRequestTime, endRequestTime); this.dlStats.calc(written, ((duration.getSeconds() * 1000) + (duration.getNano() / 1000000))); gui_.displayTransferStats(dlStats, ulStats); gui_.status(status_, 100, size); this.log.debug(String.format("File downloaded at %s/s, written %d bytes", new TransferStats(size, duration.getSeconds() + 1).getAverageSessionSpeed(), written)); this.lastRequestTime = new Date().getTime(); return Error.Type.OK; } catch (Exception e) { File destFile = new File(destination_); if (destFile.getParentFile().isDirectory() == false) { throw new FermeExceptionPathInvalid(); } else if (destFile.canWrite() == false) { throw new FermeExceptionNoWritePermission(); } else if (Utils.noFreeSpaceOnDisk(destFile.getParent(), log)) { throw new FermeExceptionNoSpaceLeftOnDevice(); } StringWriter sw = new StringWriter(); e.printStackTrace(new PrintWriter(sw)); this.log.error("Server::HTTPGetFile Exception " + e + " stacktrace " + sw.toString()); } finally { try { if (output != null) { output.flush(); output.close(); } File downloadedFile = new File(destination_ + ".partial"); if (downloadedFile.exists()) { // Rename file (or directory) boolean success = downloadedFile.renameTo(new File(destination_)); if (!success) { this.log.debug(String.format("Server::HTTPGetFile Error trying to rename the downloaded file to final name (%s)", destination_)); } } if (is != null) { is.close(); } } catch (Exception e) { this.log.debug(String.format("Server::HTTPGetFile Error trying to close the open streams (%s)", e.getMessage())); } } this.log.debug(String.format("Server::HTTPGetFile(%s) did fail", url_)); return Error.Type.DOWNLOAD_FILE; } public ServerCode HTTPSendFile(String surl, String file1, int checkpoint, Gui gui) { this.log.debug(checkpoint, "Server::HTTPSendFile(" + surl + "," + file1 + ")"); try { String fileMimeType = Utils.findMimeType(file1); File fileHandler = new File(file1); MediaType MEDIA_TYPE = MediaType.parse(fileMimeType); // e.g. "image/png" RequestBody uploadContent = new MultipartBody.Builder().setType(MultipartBody.FORM) .addFormDataPart("file", fileHandler.getName(), RequestBody.create(fileHandler, MEDIA_TYPE)).build(); Request request = new Request.Builder().addHeader("User-Agent", HTTP_USER_AGENT).url(surl).post(uploadContent).build(); LocalDateTime startRequestTime = LocalDateTime.now(); Call call = httpClient.newCall(request); Response response = call.execute(); LocalDateTime endRequestTime = LocalDateTime.now(); Duration duration = Duration.between(startRequestTime, endRequestTime); this.ulStats.calc(fileHandler.length(), ((duration.getSeconds() * 1000) + (duration.getNano() / 1000000))); gui.displayTransferStats(dlStats, ulStats); this.log.debug(String.format("File uploaded at %s/s, uploaded %d bytes", new TransferStats(fileHandler.length(), duration.getSeconds() + 1).getAverageSessionSpeed(), fileHandler.length())); int r = response.code(); String contentType = response.body().contentType().toString(); if (r == HttpURLConnection.HTTP_OK && contentType.startsWith("text/xml")) { try { String in = response.body().string(); JobValidation jobValidation = new Persister().read(JobValidation.class, in); this.lastRequestTime = new Date().getTime(); ServerCode serverCode = ServerCode.fromInt(jobValidation.getStatus()); if (serverCode != ServerCode.OK) { this.log.error(checkpoint, "Server::HTTPSendFile wrong status (is " + serverCode + ")"); return serverCode; } } catch (Exception e) { // for the .read e.printStackTrace(); } return ServerCode.OK; } else if (r == HttpURLConnection.HTTP_OK && contentType.startsWith("text/html")) { return ServerCode.ERROR_BAD_RESPONSE; } // We don't check all the HTTP 4xx but the 413 in particular, we can always find a huge image larger than whatever configuration we have in the // server and it's worth to send the error back to the server if this happen else if (r == HttpURLConnection.HTTP_ENTITY_TOO_LARGE) { this.log.error(response.body().string()); return ServerCode.JOB_VALIDATION_IMAGE_TOO_LARGE; } else { this.log.error(String.format("Server::HTTPSendFile Unknown response received from server: %s", response.body().string())); } return ServerCode.UNKNOWN; } catch (IOException e) { StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); e.printStackTrace(pw); this.log.error(checkpoint, String.format("Server::HTTPSendFile Error in upload process. Exception %s stacktrace ", e.getMessage()) + sw.toString()); return ServerCode.UNKNOWN; } catch (OutOfMemoryError e) { StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); e.printStackTrace(pw); this.log.error(checkpoint, "Server::HTTPSendFile, OutOfMemoryError " + e + " stacktrace " + sw.toString()); return ServerCode.JOB_VALIDATION_ERROR_UPLOAD_FAILED; } catch (Exception e) { StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); e.printStackTrace(pw); this.log.error(checkpoint, "Server::HTTPSendFile, Exception " + e + " stacktrace " + sw.toString()); return ServerCode.UNKNOWN; } } private String generateXMLForMD5cache() { List md5s = new ArrayList<>(); for (File local_file : this.user_config.getLocalCacheFiles()) { try { String extension = local_file.getName().substring(local_file.getName().lastIndexOf('.')).toLowerCase(); String name = local_file.getName().substring(0, local_file.getName().length() - 1 * extension.length()); if (extension.equals(".zip")) { // node_file.setAttribute("md5", name); FileMD5 fileMD5 = new FileMD5(); fileMD5.setMd5(name); md5s.add(fileMD5); } } catch (StringIndexOutOfBoundsException e) { // because the file does not have an . its path } } CacheFileMD5 cache = new CacheFileMD5(); cache.setMd5s(md5s); final Persister persister = new Persister(); try (StringWriter writer = new StringWriter()) { persister.write(cache, writer); return writer.toString(); } catch (final Exception e) { log.debug("Failed to dump md5s " + e); return ""; } } private void handleFileMD5DeleteDocument(List fileMD5s) { if (fileMD5s != null && fileMD5s.isEmpty() == false) { for (FileMD5 fileMD5 : fileMD5s) { if ("delete".equals(fileMD5.getAction()) && fileMD5.getMd5() != null && fileMD5.getMd5().isEmpty() == false) { String path = this.user_config.getWorkingDirectory().getAbsolutePath() + File.separatorChar + fileMD5.getMd5(); this.log.debug("Server::handleFileMD5DeleteDocument delete old file " + path); File file_to_delete = new File(path + ".zip"); file_to_delete.delete(); Utils.delete(new File(path)); // If we are using a shared downloads directory, then delete the file from the shared downloads directory as well :) if (this.user_config.getSharedDownloadsDirectory() != null) { String commonCacheFile = this.user_config.getSharedDownloadsDirectory().getAbsolutePath() + File.separatorChar + fileMD5.getMd5(); this.log.debug("Server::handleFileMD5DeleteDocument delete common file " + commonCacheFile + ".zip"); file_to_delete = new File(commonCacheFile + ".zip"); file_to_delete.delete(); } } } } } public String getPage(String key) { if (this.serverConfig != null) { RequestEndPoint endpoint = this.serverConfig.getRequestEndPoint(key); if (endpoint != null) { return this.base_url + endpoint.getPath(); } } return ""; } private OkHttpClient getOkHttpClient() { try { OkHttpClient.Builder builder = new OkHttpClient.Builder(); CookieManager cookieManager = new CookieManager(); cookieManager.setCookiePolicy(CookiePolicy.ACCEPT_ALL); builder.cookieJar(new JavaNetCookieJar(cookieManager)); // Cookie store to maintain the session across calls builder.connectTimeout(30, TimeUnit.SECONDS); // Cancel the HTTP Request if the connection to server takes more than 10 seconds builder.writeTimeout(60, TimeUnit.SECONDS); // Cancel the upload if the client cannot send any byte in 60 seconds // If the user has selected a proxy, then we must increase the download timeout. Reason being the way proxies work. To download a large file (i.e. // a 500MB job), the proxy must first download the file to the proxy cache and then the information is sent fast to the SheepIt client. From a client // viewpoint, the HTTP connection will make the CONNECT step really fast but then the time until the fist byte is received (the time measured by // readTimeout) will be really long (minutes). Without a proxy in the middle, a connection that does receive nothing in 60 seconds might be // considered a dead connection. if (this.user_config.getProxy() != null) { builder.readTimeout(10, TimeUnit.MINUTES); // Proxy enabled - 10 minutes } else { builder.readTimeout(1, TimeUnit.MINUTES); // No proxy - 60 seconds max } return builder.build(); } catch (Exception e) { throw new RuntimeException(e); } } }