001package gu.sql2java; 002 003import java.io.ByteArrayInputStream; 004import java.io.IOException; 005import java.io.InputStream; 006import java.lang.reflect.InvocationTargetException; 007import java.net.MalformedURLException; 008import java.net.URL; 009import java.net.URLConnection; 010import java.net.URLStreamHandler; 011import java.nio.ByteBuffer; 012import java.util.Map; 013 014import com.google.common.base.Strings; 015import com.google.common.base.Throwables; 016 017import net.gdface.url.BaseURLStore; 018import static com.google.common.base.URLParseChecks.checkURLParse; 019import static com.google.common.base.ConditionChecks.checkNotNull; 020import static com.google.common.base.ConditionChecks.checkTrue; 021 022import static com.google.common.base.Preconditions.*; 023 024import static net.gdface.utils.FaceUtilits.*; 025 026public class BaseColumnStore extends BaseURLStore { 027 028 private final Handler handler = new Handler(); 029 030 protected final String tablename; 031 032 protected final String storeColumn; 033 034 protected final TableManager<BaseBean> manager; 035 036 protected final RowMetaData metaData; 037 038 protected final Class<?> columnType; 039 040 protected final static String PKS = "PKS"; 041 042 protected BaseColumnStore(String tablename,String storeColumn) { 043 this.tablename = tablename; 044 this.storeColumn = storeColumn; 045 this.manager = Managers.managerOf(tablename); 046 this.metaData = RowMetaData.getMetaData(tablename); 047 this.columnType = metaData.columnTypeOf(storeColumn); 048 checkArgument(columnType != null, "INVALID column %s", storeColumn); 049 checkArgument(byte[].class.equals(columnType) || ByteBuffer.class.isAssignableFrom(columnType), 050 "INVALID column type of %s,byte[] or java.nio.ByteBuffer required",storeColumn); 051 052 } 053 054 @Override 055 public String getProtocol() { 056 return tablename; 057 } 058 protected URL makeURL(String suffix, Object... primaryKeys){ 059 StringBuffer buffer = new StringBuffer(storeColumn); 060 for(int i=0; i < primaryKeys.length;++i){ 061 buffer.append("/").append(primaryKeys[i]); 062 } 063 if(!Strings.isNullOrEmpty(suffix)){ 064 buffer.append('.').append(suffix); 065 } 066 try { 067 return new URL(getProtocol(),null,-1,buffer.toString()); 068 } catch (MalformedURLException e) { 069 throw new RuntimeException(e); 070 } 071 } 072 @Override 073 protected URL doStore(byte[] binary, String md5, String suffix, boolean overwrite) throws IOException { 074 try { 075 Map<String, Object> p = additionalParams.get(); 076 checkState(p != null && p.containsKey(PKS), "NOT PK defined"); 077 Object v = p.get(PKS); 078 checkState(v instanceof Object[],"INVALID PK type,Object[] required"); 079 Object[] pk = (Object[])v; 080 BaseBean bean = manager.loadByPrimaryKeyChecked(pk); 081 if(ByteBuffer.class.equals(columnType)){ 082 bean.setValue(storeColumn, ByteBuffer.wrap(binary)); 083 }else{ 084 bean.setValue(storeColumn, binary); 085 } 086 manager.save(bean); 087 return makeURL(suffix, pk); 088 } catch (Exception e) { 089 Throwables.throwIfInstanceOf(e, IOException.class); 090 throw new IOException(e); 091 } finally{ 092 additionalParams.remove(); 093 } 094 } 095 096 @Override 097 protected URL doFind(String md5) { 098 return null; 099 } 100 101 @Override 102 protected boolean doExists(URL storedURL) { 103 DatabaseURLConnection bean = new DatabaseURLConnection(storedURL).parse(); 104 return manager.existsPrimaryKey(bean.primaryKeys); 105 } 106 107 @Override 108 protected boolean doDelete(URL storedURL) throws IOException { 109 try { 110 DatabaseURLConnection bean = new DatabaseURLConnection(storedURL).parse(); 111 return manager.deleteByPrimaryKey(bean.primaryKeys) == 1; 112 } catch (Exception e) { 113 Throwables.throwIfInstanceOf(e, IOException.class); 114 throw new IOException(e); 115 } 116 } 117 118 @Override 119 protected URLStreamHandler doGetURLStreamHandler() { 120 return handler; 121 } 122 123 protected class Handler extends URLStreamHandler{ 124 125 @Override 126 protected URLConnection openConnection(URL u) throws IOException { 127 return new DatabaseURLConnection(u); 128 } 129 130 } 131 protected class DatabaseURLConnection extends URLConnection{ 132 133 private Object[] primaryKeys; 134 135 protected DatabaseURLConnection(URL url) { 136 super(url); 137 } 138 /** 139 * 解析URL为数据访问信息<br> 140 * 要求的URL格式 ${tablename}://${storeColumn}/${pk}...[/${pkN}] 141 * @return 142 * @throws URLParseException 143 */ 144 DatabaseURLConnection parse() throws URLParseException{ 145 checkTrue(getProtocol().equals(tablename), URLParseException.class, 146 "INVALID protocol ,%s reqired",tablename); 147 String[] components = url.getPath().split("/"); 148 try { 149 checkTrue(components[0].equals(storeColumn), URLParseException.class, 150 "INVALID URL,the URL's path be reqired to start with '%s'",storeColumn); 151 checkTrue(components.length == metaData.primaryKeyCount+1, URLParseException.class, 152 "MISMATCH path components acount, %s required",metaData.primaryKeyCount+1); 153 String last = components[components.length-1]; 154 int lastDot = last.lastIndexOf("."); 155 if(lastDot >= 0){ 156 // remove suffix if exists 157 components[components.length-1] = last.substring(0, lastDot); 158 } 159 primaryKeys = new Object[metaData.primaryKeyTypes.length]; 160 for(int i = 0; i < metaData.primaryKeyTypes.length;++i){ 161 primaryKeys[i] = valueOf(components[i+1], metaData.primaryKeyTypes[i]); 162 } 163 } catch (IndexOutOfBoundsException e) { 164 throw new URLParseException("URL's path lack the necessary components /${storeColumn}/${pk}...[/${pkN}]", e); 165 } catch (StringCastException e) { 166 throw new URLParseException("FAIL TO get primary key from URL's path", e); 167 } 168 checkURLParse(url != null, "NOT DEFINED store column name in ref(#)"); 169 Class<?> columnType = checkNotNull(metaData.columnTypeOf(storeColumn),URLParseException.class,"INVALID column %s",storeColumn); 170 checkURLParse(byte[].class.equals(columnType) || ByteBuffer.class.isAssignableFrom(columnType), 171 "INVALID column type of %s, binary type ( byte[] or java.nio.ByteBuffer) required",storeColumn); 172 return this; 173 } 174 175 @Override 176 public void connect() throws IOException { 177 try { 178 parse(); 179 connected = true; 180 } catch (Exception e) { 181 Throwables.throwIfInstanceOf(e, IOException.class); 182 throw new IOException(e); 183 } 184 } 185 @Override 186 public InputStream getInputStream() throws IOException{ 187 connect(); 188 try { 189 BaseBean bean = manager.loadByPrimaryKeyChecked(primaryKeys); 190 Object data = bean.getValue(storeColumn); 191 return new ByteArrayInputStream(getBytesNotEmpty(data)); 192 } catch (Exception e) { 193 Throwables.throwIfInstanceOf(e, IOException.class); 194 throw new IOException(e); 195 } 196 } 197 } 198 @SuppressWarnings("unchecked") 199 private static <T> T valueOf(String input,Class<T> targetType)throws StringCastException{ 200 if(Strings.isNullOrEmpty(input) || String.class.equals(targetType)){ 201 return (T) input; 202 } 203 checkNotNull(targetType != null, StringCastException.class,"target is null"); 204 try { 205 return (T) targetType.getMethod("valueOf", String.class).invoke(null, input); 206 } catch (IllegalAccessException | NoSuchMethodException | SecurityException e) { 207 } catch (InvocationTargetException e) { 208 throw new StringCastException(e.getTargetException()); 209 } 210 throw new StringCastException(String.format("INVALID target type %s",targetType)); 211 } 212 public static class URLParseException extends RuntimeException{ 213 private static final long serialVersionUID = 1L; 214 215 public URLParseException(Throwable cause) { 216 super(cause); 217 } 218 219 public URLParseException(String message, Throwable cause) { 220 super(message, cause); 221 } 222 223 public URLParseException(String message) { 224 super(message); 225 } 226 } 227 public static class StringCastException extends RuntimeException{ 228 private static final long serialVersionUID = 1L; 229 230 public StringCastException(Throwable cause) { 231 super(cause); 232 } 233 234 public StringCastException(String message) { 235 super(message); 236 } 237 } 238} 239