/* * Copyright 2008-2014 the original author or authors. * * 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 org.kaleidofoundry.core.store; import static org.kaleidofoundry.core.store.FileStoreConstants.DEFAULT_BUFFER_SIZE; import static org.kaleidofoundry.core.store.FileStoreConstants.DEFAULT_CHARSET; import static org.kaleidofoundry.core.store.FileStoreContextBuilder.BufferSize; import static org.kaleidofoundry.core.store.FileStoreContextBuilder.Charset; import java.io.BufferedInputStream; import java.io.BufferedReader; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; import java.io.Reader; import java.net.URI; import java.nio.ByteBuffer; import java.nio.channels.Channels; import org.kaleidofoundry.core.context.RuntimeContext; import org.kaleidofoundry.core.io.FileHelper; import org.kaleidofoundry.core.io.MimeTypeResolver; import org.kaleidofoundry.core.io.MimeTypeResolverFactory; import org.kaleidofoundry.core.lang.annotation.NotYetImplemented; import org.kaleidofoundry.core.util.StringHelper; import com.google.appengine.api.files.AppEngineFile; import com.google.appengine.api.files.FileReadChannel; import com.google.appengine.api.files.FileService; import com.google.appengine.api.files.FileServiceFactory; import com.google.appengine.api.files.FileWriteChannel; import com.google.appengine.api.files.GSFileOptions.GSFileOptionsBuilder; /** * Google application file store service * <br/> * <b>References</b> * <ul> * <li>https://developers.google.com/appengine/docs/java/javadoc/com/google/appengine/api/files/FileService</li> * <li>https://developers.google.com/storage/docs/hellogooglestorage/li> * <li>https://developers.google.com/appengine/docs/java/javadoc/com/google/appengine/api/files/AppEngineFile/li> * <li>https://developers.google.com/appengine/docs/java/javadoc/com/google/appengine/api/files/AppEngineFile.FileSystem/li> * </ul> * @author jraduget */ public class GaeFileStore extends AbstractFileStore { protected final FileService fileService = FileServiceFactory.getFileService(); /** * @param context */ public GaeFileStore(final RuntimeContext<FileStore> context) { super(context); } /** * @param baseUri * @param context */ public GaeFileStore(final String baseUri, final RuntimeContext<FileStore> context) { super(baseUri, context); } /* * (non-Javadoc) * @see org.kaleidofoundry.core.store.AbstractFileStore#getStoreType() */ @Override public FileStoreType[] getStoreType() { return new FileStoreType[] { FileStoreTypeEnum.gs }; } /* * (non-Javadoc) * @see org.kaleidofoundry.core.store.AbstractFileStore#doGet(java.net.URI) */ @Override protected ResourceHandler doGet(final URI resourceUri) throws ResourceNotFoundException, ResourceException { FileReadChannel readChannel = null; ResourceHandler resource; MimeTypeResolver mimeTypeResolver = MimeTypeResolverFactory.getService(); try { final String filepath = "/gs" + resourceUri.getPath(); final AppEngineFile readableFile = new AppEngineFile(filepath); final String mimeType = mimeTypeResolver.getMimeType(FileHelper.getFileNameExtension(resourceUri.getPath())); final String charset = context.getString(Charset, DEFAULT_CHARSET.getCode()); final int bufferSize = context.getInteger(BufferSize, DEFAULT_BUFFER_SIZE); readChannel = fileService.openReadChannel(readableFile, false); LOGGER.debug("gs filepath={}", filepath); if (mimeTypeResolver.isText(mimeType)) { LOGGER.debug("gs content type isText={}", true); //resource = createResourceHandler(resourceUri.toString(), readChannel, Channels.newReader(readChannel, charset), charset); resource = createResourceHandler(resourceUri.toString(), readChannel, new BufferedReader(Channels.newReader(readChannel, charset), bufferSize), charset); } else { LOGGER.debug("gs content type isText={}", false); //resource = createResourceHandler(resourceUri.toString(), readChannel, Channels.newInputStream(readChannel)); resource = createResourceHandler(resourceUri.toString(), readChannel, new BufferedInputStream(Channels.newInputStream(readChannel), bufferSize)); } // Set some meta datas if (resource instanceof ResourceHandlerBean) { // ((ResourceHandlerBean) resource).setLastModified(file.lastModified()); ((ResourceHandlerBean) resource).setMimeType(mimeType); } return resource; } catch (final FileNotFoundException fnfe) { throw new ResourceNotFoundException(resourceUri.toString()); } catch (final IOException ioe) { throw new ResourceException(resourceUri.toString(), ioe); } } /* * (non-Javadoc) * @see org.kaleidofoundry.core.store.AbstractFileStore#doRemove(java.net.URI) */ @Override @NotYetImplemented protected void doRemove(final URI resourceUri) throws ResourceNotFoundException, ResourceException { final String filepath = "/gs" + resourceUri.getPath(); final AppEngineFile readableFile = new AppEngineFile(filepath); try { fileService.delete(readableFile); } catch (FileNotFoundException fnfe) { throw new ResourceNotFoundException(resourceUri.toString()); } catch (IOException ioe) { throw new ResourceException(ioe, resourceUri.toString()); } } /* * (non-Javadoc) * @see org.kaleidofoundry.core.store.AbstractFileStore#doStore(java.net.URI, org.kaleidofoundry.core.store.ResourceHandler) */ @Override protected void doStore(final URI resourceUri, final ResourceHandler resource) throws ResourceException { String bucketName = getBucketName(); String charset = context.getString(Charset, DEFAULT_CHARSET.getCode()); boolean isTextContent = false; MimeTypeResolver mimeTypeResolver = MimeTypeResolverFactory.getService(); GSFileOptionsBuilder optionsBuilder = new GSFileOptionsBuilder().setBucket(bucketName).setKey(resource.getUri()); if (!StringHelper.isEmpty(resource.getMimeType())) { optionsBuilder.setMimeType(resource.getMimeType()); isTextContent = mimeTypeResolver.isText(resource.getMimeType()); } // optionsBuilder.setAcl("public_read"); if (isTextContent) { optionsBuilder.setContentEncoding(charset); } final AppEngineFile writableFile; FileWriteChannel writeChannel = null; try { writableFile = fileService.createNewGSFile(optionsBuilder.build()); writeChannel = fileService.openWriteChannel(writableFile, true); if (isTextContent) { final char[] cbuff = new char[context.getInteger(BufferSize, DEFAULT_BUFFER_SIZE)]; int lengthToWrite = resource.getReader(charset).read(cbuff); PrintWriter out = new PrintWriter(Channels.newWriter(writeChannel, charset)); while (lengthToWrite != -1) { out.write(cbuff, 0, lengthToWrite); lengthToWrite = resource.getReader(charset).read(cbuff); } out.close(); } else { final byte[] buff = new byte[context.getInteger(BufferSize, DEFAULT_BUFFER_SIZE)]; int lengthToWrite = resource.getInputStream().read(buff); while (lengthToWrite != -1) { writeChannel.write(ByteBuffer.wrap(buff, 0, lengthToWrite)); lengthToWrite = resource.getInputStream().read(buff); } } } catch (final IOException ioe) { throw new ResourceException(ioe, resourceUri.toString()); } finally { if (writeChannel != null) { try { writeChannel.closeFinally(); } catch (final IOException ioe) { throw new ResourceException(ioe, resourceUri.toString()); } } } } public ResourceHandler createResourceHandler(final String resourceUri, final FileReadChannel fileReadChannel, final Reader reader, final String charset) { ResourceHandler resource = new GaeResourceHandlerBean(this, resourceUri, fileReadChannel, reader, charset); openedResources.put(resourceUri, resource); return resource; } public ResourceHandler createResourceHandler(final String resourceUri, final FileReadChannel fileReadChannel, final InputStream input) { ResourceHandler resource = new GaeResourceHandlerBean(this, resourceUri, fileReadChannel, input); openedResources.put(resourceUri, resource); return resource; } /** * @return gs bucket name of the resource store (it is extracted from the resource store base dir */ protected String getBucketName() { String bucketName = getBaseUri(); for (FileStoreType type : getStoreType()) { if (bucketName.startsWith(type.name())) { bucketName = bucketName.replace(type.name(), ""); break; } } bucketName = bucketName.replaceFirst(":/", ""); bucketName = bucketName.replaceFirst("://", ""); return bucketName; } }