001package com.killcoding.jsplus;
002
003import java.util.concurrent.ExecutorService;
004import com.killcoding.jsplus.ScriptConsole;
005import java.util.concurrent.Executors;
006import com.killcoding.log.LoggerFactory;
007import com.killcoding.log.Logger;
008import com.killcoding.datasource.CacheDriverExecutor;
009import java.sql.Connection;
010import com.killcoding.datasource.DriverConnection;
011import com.killcoding.datasource.DriverDataSource;
012import java.sql.SQLException;
013import com.killcoding.cache.CacheArray;
014import java.util.HashMap;
015import java.util.Map;
016import com.killcoding.cache.CacheArrayFilter;
017import com.killcoding.tool.ConfigProperties;
018import java.io.File;
019import java.io.IOException;
020import com.killcoding.datasource.Clock;
021import java.sql.Timestamp;
022import com.killcoding.tool.CommonTools;
023
024/**
025 * 
026    CREATE TABLE ghost_remote_command (
027        id CHAR(16) PRIMARY KEY,
028        ghost_name VARCHAR(200),
029        request_script VARCHAR(500),
030        response_message VARCHAR(500),
031        processed INT,
032        requested INT,
033        responsed INT,
034        created_at TIMESTAMP,
035        updated_at TIMESTAMP,
036        deleted_at TIMESTAMP,
037        deleted SMALLINT
038    );
039 * 
040 */
041public class GhostConsole extends ScriptConsole {
042    
043    public static int RESULT_SIZE = 500;
044    
045    private static String GHOST_REQUESTED_SQL = "SELECT * FROM %s WHERE deleted = 0 AND requested = 1 AND responsed = 0 AND processed = 0 AND ghost_name = :ghost_name ORDER BY created_at ASC";
046    private static String GHOST_GET_PROCESSED_SQL = "SELECT * FROM %s WHERE deleted = 0 AND requested = 1 AND responsed = 0 AND processed = 1 AND ghost_name = :ghost_name ORDER BY created_at ASC";
047    private static String GHOST_PROCESSED_SQL = "UPDATE %s SET responsed = 0,processed = 1,response_message = :response_message,updated_at = :updated_at WHERE id = :id";
048    private static String GHOST_RESPONSED_SQL = "UPDATE %s SET responsed = 1,updated_at = :updated_at WHERE id = :id";
049    private static String GHOST_REQUEST_SQL = "INSERT INTO %s (id,ghost_name,request_script,response_message,requested,responsed,processed,created_at,updated_at,deleted_at,deleted) VALUES (:id,:ghost_name,:request_script,:response_message,:requested,:responsed,:processed,:created_at,:updated_at,:deleted_at,:deleted)";
050    private static String GHOST_CLEAN_SQL = "DELETE FROM %s WHERE responsed = 1 AND processed = 1 AND deleted = 0 AND ghost_name = :ghost_name";   
051    private static String GHOST_CLEAN_ALL_SQL = "DELETE FROM %s WHERE ghost_name = :ghost_name";
052           
053    private ConfigProperties config = null;
054    private DriverDataSource ghostDs = null;
055    
056    private String ghostTable = null;
057    private String ghostName = null;
058    private long ghostTimer = 200L;
059    private CacheArray requestRows = null;
060    private CacheArray responseRows = null;
061    private ScriptConsole console = null;
062    private boolean autoClean = true;
063   
064    private boolean exit = false;
065    private boolean firstStart = true;
066    
067    public GhostConsole(ScriptConsole console,String ghostName) {
068        super();
069        this.console = console;
070        this.ghostName = ghostName;
071        this.setUseGhostEnv();
072        CacheDriverExecutor.COLUMN_NAME_CASE_MODE = CacheDriverExecutor.COLUMN_NAME_CASE_LOWER;
073        String ghostDsPropFile = this.console.getJsPlusPath() + "/config/GhostDataSource.properties";
074        init(ghostDsPropFile);
075    }
076    
077    private void init(String ghostDsPropFile) {
078        try{
079            config = new ConfigProperties();
080            ghostDs = new DriverDataSource(new File(ghostDsPropFile));
081            config = ghostDs.getConfigProperties();
082            ghostTable = config.getString("GhostTable","ghost_remote_command");
083            ghostTimer = config.getMilliSeconds("GhostTimer",200L);            
084        }catch(Exception e){
085            Logger.systemError(GhostConsole.class,e.getMessage(),e);
086        }
087    }
088    
089    public boolean isAutoClean(){
090        return this.autoClean;
091    }
092    
093    public void submit(String executeCommand){
094        String sql = String.format(GHOST_REQUEST_SQL,ghostTable);
095        CacheDriverExecutor cde = null;
096        try{
097            Map<String,Object> params = new HashMap<String,Object>();    
098            Timestamp currentTime = new Clock().getSqlTimestamp();
099            params.put("ghost_name",ghostName);
100            params.put("requested",1);
101            params.put("responsed",0);
102            params.put("processed",0);
103            params.put("request_script",executeCommand);
104            params.put("response_message",null);
105            params.put("created_at",currentTime);
106            params.put("updated_at",currentTime);
107            params.put("deleted",0);
108            params.put("id",CommonTools.generateId(16));
109            Connection conn = ghostDs.getConnection();
110            cde = new CacheDriverExecutor(conn);
111            cde.execute(sql,params);
112        }catch(Exception e){
113           Logger.systemError(GhostConsole.class,e.getMessage(),e); 
114        }finally{
115           if(cde != null) try{cde.close();}catch(Exception e){}
116        }
117    }
118    
119    private Runnable getExecuteRun(){
120        return new Runnable(){
121            @Override
122            public void run(){
123                CacheDriverExecutor cde = null;
124                try{
125                    Object o = requestRows.getObject();
126                    if(o != null){
127                        Map<String,Object> ghostRecord = (Map<String,Object>)o;
128                        String requestScript = (String)ghostRecord.get("request_script");
129                        Object id = ghostRecord.get("id");
130                        Object result = null;
131                        try{
132                            result = console.evalScript(requestScript);
133                        }catch(Exception e){
134                            result = e.getMessage();
135                            Logger.systemError(GhostConsole.class,e.getMessage(),e);
136                        }
137                        result = (result == null ? null : result + "");
138                        if(result != null && result.toString().length() > RESULT_SIZE){
139                            result = result.toString().substring(0,RESULT_SIZE - 1);
140                        }
141                        Map<String,Object> params = new HashMap<String,Object>();
142                        String sql = String.format(GHOST_PROCESSED_SQL,ghostTable);
143                        params = new HashMap<String,Object>();
144                        params.put("response_message",result);
145                        params.put("updated_at",new Clock().getSqlTimestamp());
146                        params.put("id",id);
147                        Connection conn = ghostDs.getConnection();
148                        cde = new CacheDriverExecutor(conn);
149                        cde.execute(sql,params);
150                    }
151                }catch(Exception e){
152                    Logger.systemError(GhostConsole.class,e.getMessage(),e);
153                }finally{
154                    if(cde != null) try{cde.close();}catch(Exception e){}
155                } 
156            }
157        };
158    }
159    
160    private Runnable getCompletedRun(){
161        final GhostConsole the = this;
162        return new Runnable(){
163            @Override
164            public void run(){
165                try{
166                    the.requestRows = null;
167
168                    if(!exit) Thread.sleep(ghostTimer);
169                    
170                    if(!exit) the.start(the.autoClean);
171                }catch(Exception e){
172                    Logger.systemError(GhostConsole.class,e.getMessage(),e);
173                    
174                    if(!exit) try {Thread.sleep(ghostTimer);} catch(Exception ee) {};
175                    
176                    if(!exit) the.start(the.autoClean);
177                }
178            }
179        };
180    }    
181
182   private Runnable getDetectExecuteRun(){
183        return new Runnable(){
184            @Override
185            public void run(){
186                CacheDriverExecutor cde = null;
187                try{
188                    Object o = responseRows.getObject();
189                    if(o != null){
190                        Map<String,Object> ghostRecord = (Map<String,Object>)o;
191                        String responseMessge = (String)ghostRecord.get("response_message");
192                        Object id = ghostRecord.get("id");
193                        String sql = String.format(GHOST_RESPONSED_SQL,ghostTable);
194                        Map<String,Object> params = new HashMap<String,Object>();
195                        params.put("response_message",null);
196                        params.put("updated_at",new Clock().getSqlTimestamp());
197                        params.put("id",id);
198                        Connection conn = ghostDs.getConnection();
199                        cde = new CacheDriverExecutor(conn);
200                        cde.execute(sql,params);
201                        if((responseMessge + "").equals("undefined")){
202                            responseMessge = null;
203                        }
204                        if(responseMessge == null){
205                             System.out.println("The command has been executed remotely but no message has been returned.");
206                        }else{
207                             String[] responseMessgeArray = responseMessge.split("\n");
208                             int len = responseMessgeArray.length;
209                             for(int i = 0;i < len;i++){
210                                 System.out.println(responseMessgeArray[i]);
211                                 Thread.sleep(200L);
212                             }
213                        }
214                    }
215                }catch(Exception e){
216                    Logger.systemError(GhostConsole.class,e.getMessage(),e);
217                }finally{
218                    if(cde != null) try{cde.close();}catch(Exception e){}
219                } 
220            }
221        };
222    }
223    
224    private Runnable getDetectCompletedRun(){
225        final GhostConsole the = this;
226        return new Runnable(){
227            @Override
228            public void run(){
229                try{
230                    the.responseRows = null;
231                    
232                    if(!exit) Thread.sleep(ghostTimer);
233                    
234                    if(!exit) the.detect();
235                }catch(Exception e){
236                    Logger.systemError(GhostConsole.class,e.getMessage(),e);
237                    
238                    if(!exit) try {Thread.sleep(ghostTimer);} catch(Exception ee) {};
239                    
240                    if(!exit) the.detect();                    
241                }
242            }
243        };
244    }      
245    
246    public synchronized void detect() {
247        GhostConsole the = this;
248        if(responseRows == null){
249            CacheDriverExecutor cde = null;
250            try{
251                String sql = String.format(GHOST_GET_PROCESSED_SQL,ghostTable);
252                responseRows = new CacheArray();
253                Runnable executeRun = getDetectExecuteRun();
254                Runnable completedRun = getDetectCompletedRun();
255                responseRows.filter(ghostTimer,executeRun,completedRun,null);
256                Map<String,Object> params = new HashMap<String,Object>();
257                params.put("ghost_name",ghostName);
258                Connection conn = ghostDs.getConnection();
259                cde = new CacheDriverExecutor(conn);
260                cde.find(0,100,sql,params,responseRows);
261            }catch(Exception e){
262                Logger.systemError(GhostConsole.class,e.getMessage(),e);
263                
264                if(!exit) try {Thread.sleep(ghostTimer);} catch(Exception ee) {};
265                
266                if(!exit) the.detect();
267            }finally{
268                if(cde != null) try {cde.close();}catch(Exception ee){};
269            }
270        }
271    }  
272    
273    public void start() throws Exception {
274        start(true);
275    }    
276    
277    public synchronized void start(boolean autoClean) {
278        this.autoClean = autoClean;
279        GhostConsole the = this;
280        if(requestRows == null){
281            CacheDriverExecutor cde = null;
282            try{
283                String sql = String.format(GHOST_REQUESTED_SQL,ghostTable);
284                String cleanSql = String.format(GHOST_CLEAN_SQL,ghostTable);
285                requestRows = new CacheArray();
286                Runnable executeRun = getExecuteRun();
287                Runnable completedRun = getCompletedRun();
288                requestRows.filter(ghostTimer,executeRun,completedRun,null);
289                Map<String,Object> params = new HashMap<String,Object>();
290                params.put("ghost_name",ghostName);
291                Connection conn = ghostDs.getConnection();
292                cde = new CacheDriverExecutor(conn);
293                if(firstStart){
294                    firstStart = false;
295                    if(this.autoClean){
296                        String cleanAllSql = String.format(GHOST_CLEAN_ALL_SQL,ghostTable);
297                        cde.execute(cleanAllSql,params);
298                    }
299                }
300                cde.execute(cleanSql,params);
301                cde.find(0,100,sql,params,requestRows);
302            }catch(Exception e){
303                Logger.systemError(GhostConsole.class,e.getMessage(),e);
304                
305                if(!exit) try {Thread.sleep(ghostTimer);} catch(Exception ee) {};
306                
307                if(!exit) the.start(the.autoClean);
308            }finally{
309                if(cde != null) try {cde.close();}catch(Exception ee){};
310            }
311        }
312    }
313    
314    public synchronized void stop(){
315        exit = true;
316    }
317}