Java tutorial
/** * personium.io * Copyright 2014 FUJITSU LIMITED * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.fujitsu.dc.core.bar; import java.io.File; import java.io.FileDescriptor; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.SyncFailedException; import java.io.UnsupportedEncodingException; import java.util.Enumeration; import java.util.LinkedHashMap; import java.util.Map; import java.util.zip.ZipException; import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.ResponseBuilder; import javax.ws.rs.core.UriInfo; import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; import org.apache.commons.compress.archivers.zip.ZipFile; import org.apache.commons.io.IOUtils; import org.apache.http.HttpStatus; import org.json.simple.JSONObject; import org.json.simple.parser.ParseException; import org.odata4j.expression.BoolCommonExpression; import org.odata4j.producer.QueryInfo; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.fujitsu.dc.core.DcCoreConfig; import com.fujitsu.dc.core.DcCoreException; import com.fujitsu.dc.core.auth.AccessContext; import com.fujitsu.dc.core.auth.CellPrivilege; import com.fujitsu.dc.core.bar.jackson.JSONManifest; import com.fujitsu.dc.core.model.Box; import com.fujitsu.dc.core.model.Cell; import com.fujitsu.dc.core.odata.DcODataProducer; import com.fujitsu.dc.core.odata.DcOptionsQueryParser; import com.fujitsu.dc.core.rs.odata.ODataEntityResource; import com.fujitsu.dc.core.rs.odata.ODataResource; /** * bar??. */ public class BarFileInstaller { /** * . */ static Logger log = LoggerFactory.getLogger(BarFileInstaller.class); static final long MB = 1024 * 1024; static final int BUF_SIZE = 1024; // for output response. private final Cell cell; private String boxName; private ODataEntityResource oDataEntityResource; private UriInfo uriInfo; private String barTempDir = DcCoreConfig.get(DcCoreConfig.BAR.BAR_INSTALLFILE_DIR); private JSONObject manifestJson; /** * . * @param cell * * @param boxName * ?? * @param oDataEntityResource oDataEntityResource * @param uriInfo UriInfo */ public BarFileInstaller(final Cell cell, final String boxName, final ODataEntityResource oDataEntityResource, final UriInfo uriInfo) { this.cell = cell; this.boxName = boxName; this.oDataEntityResource = oDataEntityResource; this.uriInfo = uriInfo; } /** * bar?. * @param headers * Http???MAP * @param inStream * HttpInputStream * @param requestKey ??RequestKey? * @return ? */ public Response barFileInstall(Map<String, String> headers, InputStream inStream, String requestKey) { // ?? checkPreConditions(headers); // bar?? File barFile = storeTemporaryBarFile(inStream); BarFileReadRunner runner = null; try { // bar?? long entryCount = checkBarFileContents(barFile); // Box??URL??? checkDuplicateBoxAndSchema(); // Box?? // ??????400?????Box????????Box???????? runner = new BarFileReadRunner(barFile, this.cell, this.boxName, this.oDataEntityResource, this.oDataEntityResource.getOdataProducer(), Box.EDM_TYPE_NAME, this.uriInfo, requestKey); runner.createBox(this.manifestJson); // bar????ProgressInfo? runner.setEntryCount(entryCount); runner.writeInitProgressCache(); } catch (DcCoreException e) { if (null != runner) { runner.writeErrorProgressCache(); } removeBarFile(barFile); throw e; } catch (Exception e) { if (null != runner) { runner.writeErrorProgressCache(); } removeBarFile(barFile); throw DcCoreException.Server.UNKNOWN_ERROR; } finally { IOUtils.closeQuietly(inStream); } // ?? // TODO ????????? Thread thread = new Thread(runner); thread.start(); // ??? ResponseBuilder res = Response.status(HttpStatus.SC_ACCEPTED); res.header(HttpHeaders.LOCATION, this.cell.getUrl() + boxName); return res.build(); } private void removeBarFile(File barFile) { if (barFile.exists() && !barFile.delete()) { log.warn("Failed to remove bar file. [" + barFile.getAbsolutePath() + "]."); } } /** * bar?????. * @param headers HTTP */ private void checkPreConditions(Map<String, String> headers) { // [403] AccessContext accessContext = this.oDataEntityResource.getAccessContext(); ODataResource odataResource = this.oDataEntityResource.getOdataResource(); odataResource.checkAccessContext(accessContext, CellPrivilege.BOX_BAR_INSTALL); // [400]??? checkHeaders(headers); } /** * Http??. * @param headers * Http???MAP */ private void checkHeaders(Map<String, String> headers) { // Content-Type: application/zip String contentType = headers.get(HttpHeaders.CONTENT_TYPE); if (!"application/zip".equals(contentType)) { throw DcCoreException.BarInstall.REQUEST_HEADER_FORMAT_ERROR.params(HttpHeaders.CONTENT_TYPE); } } /** * ????bar?(MB)?? * @return com.fujitsu.dc.core.bar.file.maxSize */ protected long getMaxBarFileSize() { long maxBarFileSize; try { maxBarFileSize = Long.parseLong(DcCoreConfig.get(DcCoreConfig.BAR.BAR_FILE_MAX_SIZE)); } catch (NumberFormatException ne) { throw DcCoreException.Server.UNKNOWN_ERROR; } return maxBarFileSize; } /** * ?BAR?(MB)?? * @return com.fujitsu.dc.core.bar.entry.maxSize */ protected long getMaxBarEntryFileSize() { long maxBarFileSize; try { maxBarFileSize = Long.parseLong(DcCoreConfig.get(DcCoreConfig.BAR.BAR_ENTRY_MAX_SIZE)); } catch (NumberFormatException ne) { log.info("NumberFormatException" + DcCoreConfig.get(DcCoreConfig.BAR.BAR_ENTRY_MAX_SIZE)); throw DcCoreException.Server.UNKNOWN_ERROR; } return maxBarFileSize; } /** * ??. * @param fd * @throws SyncFailedException ?? */ public void sync(FileDescriptor fd) throws SyncFailedException { fd.sync(); } /** * Http?bar??????. * @param inStream HttpInputStream * @return ????bar?File */ private File storeTemporaryBarFile(InputStream inStream) { // bar????????? String unitUserName = BarFileUtils.getUnitUserName(this.cell.getOwner()); File barFileDir = new File(new File(barTempDir, unitUserName), "bar"); if (!barFileDir.exists() && !barFileDir.mkdirs()) { String message = "unable create directory: " + barFileDir.getAbsolutePath(); throw DcCoreException.Server.FILE_SYSTEM_ERROR.params(message); } // barNFS??? String prefix = this.cell.getId() + "_" + this.boxName; File barFile = null; OutputStream outStream = null; try { barFile = File.createTempFile(prefix, ".bar", barFileDir); barFile.deleteOnExit(); // VM?? outStream = new FileOutputStream(barFile); IOUtils.copyLarge(inStream, outStream); } catch (IOException e) { String message = "unable save bar file: %s"; if (barFile == null) { message = String.format(message, barFileDir + prefix + "XXX.bar"); } else { message = String.format(message, barFile.getAbsolutePath()); } throw DcCoreException.Server.FILE_SYSTEM_ERROR.params(message); } finally { if (null != outStream && DcCoreConfig.getFsyncEnabled()) { try { sync(((FileOutputStream) outStream).getFD()); } catch (Exception e) { throw DcCoreException.Server.FILE_SYSTEM_ERROR.params(e.getMessage()); } } IOUtils.closeQuietly(outStream); } return barFile; } /** * bar?????. * <ul> * <li>bar????</li> * <li>bar???????</li> * <li>TODO bar??????</li> * </ul>. * @param barFile ????bar?File * @returns bar? */ private long checkBarFileContents(File barFile) { // bar? checkBarFileSize(barFile); ZipFile zipFile = null; try { zipFile = new ZipFile(barFile, "UTF-8"); Enumeration<ZipArchiveEntry> entries = zipFile.getEntries(); ZipArchiveEntry zae = null; long entryCount = 0; String entryName = null; try { long maxBarEntryFileSize = getMaxBarEntryFileSize(); // ?? Map<String, String> requiredBarFiles = setupBarFileOrder(); while (entries.hasMoreElements()) { zae = entries.nextElement(); entryName = zae.getName(); log.info("read: " + entryName); if (!zae.isDirectory()) { // ??????bar? entryCount++; // bar?? checkBarFileEntrySize(zae, entryName, maxBarEntryFileSize); // Box????? if (zae.getName().endsWith("/" + BarFileReadRunner.MANIFEST_JSON)) { checkAndReadManifest(entryName, zae, zipFile); } } // bar??????? if (!checkBarFileStructures(zae, requiredBarFiles)) { throw DcCoreException.BarInstall.BAR_FILE_INVALID_STRUCTURES.params(entryName); } } if (!requiredBarFiles.isEmpty()) { StringBuilder entryNames = new StringBuilder(); Object[] requiredFileNames = requiredBarFiles.keySet().toArray(); for (int i = 0; i < requiredFileNames.length; i++) { if (i > 0) { entryNames.append(" " + requiredFileNames[i]); } else { entryNames.append(requiredFileNames[i]); } } throw DcCoreException.BarInstall.BAR_FILE_INVALID_STRUCTURES.params(entryNames.toString()); } return entryCount; } catch (DcCoreException e) { throw e; } catch (Exception e) { log.info(e.getMessage(), e.fillInStackTrace()); throw DcCoreException.BarInstall.BAR_FILE_CANNOT_READ.params(entryName); } } catch (FileNotFoundException e) { throw DcCoreException.BarInstall.BAR_FILE_CANNOT_OPEN.params("barFile"); } catch (ZipException e) { throw DcCoreException.BarInstall.BAR_FILE_CANNOT_OPEN.params(e.getMessage()); } catch (IOException e) { throw DcCoreException.BarInstall.BAR_FILE_CANNOT_OPEN.params(e.getMessage()); } catch (DcCoreException e) { throw e; } catch (RuntimeException e) { throw DcCoreException.Server.UNKNOWN_ERROR; } finally { ZipFile.closeQuietly(zipFile); } } private void checkAndReadManifest(String entryName, ZipArchiveEntry zae, ZipFile zipFile) throws IOException { InputStream inStream = zipFile.getInputStream(zae); try { JSONManifest manifest = BarFileUtils.readJsonEntry(inStream, entryName, JSONManifest.class); if (!manifest.checkSchema()) { throw DcCoreException.BarInstall.BAR_FILE_INVALID_STRUCTURES.params(entryName); } this.manifestJson = manifest.getJson(); } finally { IOUtils.closeQuietly(inStream); } } /** * bar??. * @param zae bar * @param entryName ?? * @param maxBarEntryFileSize ? */ protected void checkBarFileEntrySize(ZipArchiveEntry zae, String entryName, long maxBarEntryFileSize) { // [400]bar?????? if (zae.getSize() > (long) (maxBarEntryFileSize * MB)) { String message = "Bar file entry size too large invalid file [%s: %sB]"; log.info(String.format(message, entryName, String.valueOf(zae.getSize()))); throw DcCoreException.BarInstall.BAR_FILE_ENTRY_SIZE_TOO_LARGE.params(entryName, String.valueOf(zae.getSize())); } } /** * bar?. * @param barFile bar */ protected void checkBarFileSize(File barFile) { // [400]bar?????? long maxBarFileSize = getMaxBarFileSize(); if (barFile.length() > (long) (maxBarFileSize * MB)) { String message = "Bar file size too large invalid file [%sB]"; log.info(String.format(message, String.valueOf(barFile.length()))); throw DcCoreException.BarInstall.BAR_FILE_SIZE_TOO_LARGE.params(String.valueOf(barFile.length())); } } /** * bar?. */ private Map<String, String> setupBarFileOrder() { Map<String, String> requiredBarFiles = new LinkedHashMap<String, String>(); requiredBarFiles.put("bar/", BarFileReadRunner.ROOT_DIR); requiredBarFiles.put("bar/00_meta/", BarFileReadRunner.META_DIR); requiredBarFiles.put("bar/00_meta/00_manifest.json", BarFileReadRunner.MANIFEST_JSON); requiredBarFiles.put("bar/00_meta/90_rootprops.xml", BarFileReadRunner.ROOTPROPS_XML); return requiredBarFiles; } /** * bar???. */ private boolean checkBarFileStructures(ZipArchiveEntry zae, Map<String, String> requiredBarFiles) throws UnsupportedEncodingException, ParseException { String entryName = zae.getName(); // ex. "bar/00_meta/00_manifest.json" if (requiredBarFiles.containsKey(entryName)) { requiredBarFiles.remove(entryName); } return true; } /** * Box??????????????URL???????????. */ private void checkDuplicateBoxAndSchema() { DcODataProducer producer = oDataEntityResource.getOdataProducer(); // [400]???scheme URL???Box???? // ??URL??Box??1?????? String schemaUrl = (String) this.manifestJson.get("Schema"); BoolCommonExpression filter = DcOptionsQueryParser.parseFilter("Schema eq '" + schemaUrl + "'"); QueryInfo query = new QueryInfo(null, null, null, filter, null, null, null, null, null); if (producer.getEntitiesCount(Box.EDM_TYPE_NAME, query).getCount() > 0) { throw DcCoreException.BarInstall.BAR_FILE_BOX_SCHEMA_ALREADY_EXISTS.params(schemaUrl); } // [405]?????Box???? // Box????????????????? filter = DcOptionsQueryParser.parseFilter("Name eq '" + this.boxName + "'"); query = new QueryInfo(null, null, null, filter, null, null, null, null, null); if (producer.getEntitiesCount(Box.EDM_TYPE_NAME, query).getCount() > 0) { throw DcCoreException.BarInstall.BAR_FILE_BOX_ALREADY_EXISTS.params(this.boxName); } log.info("Install target Box is not found, able to install."); } /** * ????. * @return ?? */ public String getCellName() { return cell.getName(); } /** * ODataProducer??. * @return ODataProducer */ public DcODataProducer getOdataProducer() { return oDataEntityResource.getOdataProducer(); } /** * ODataEntityResource??. * @return ODataEntityResource */ public ODataEntityResource getODataEntityResource() { return oDataEntityResource; } /** * URI??. * @return UriInfo */ public UriInfo getUriInfo() { return uriInfo; } }