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