001// Copyright(c) 2015 POSCO ICT
002// Change history
003// 2015-07-10 / 1.1.0 / 
004package com.poscoict.app.job;
005
006import java.lang.management.ManagementFactory;
007import java.lang.reflect.InvocationTargetException;
008import java.lang.reflect.Method;
009import java.sql.Connection;
010import java.sql.PreparedStatement;
011import java.sql.SQLException;
012import java.util.Date;
013import java.util.List;
014import java.util.Map;
015
016import javax.sql.DataSource;
017
018import org.apache.commons.dbcp2.BasicDataSource;
019import org.slf4j.Logger;
020import org.slf4j.LoggerFactory;
021import org.springframework.context.ApplicationContext;
022import org.springframework.context.support.ClassPathXmlApplicationContext;
023import org.springframework.jdbc.datasource.DataSourceUtils;
024import org.springframework.jdbc.support.JdbcUtils;
025
026import com.poscoict.glueframework.biz.control.GlueBizControlConstants;
027import com.poscoict.glueframework.biz.control.GlueBizProvider;
028import com.poscoict.glueframework.context.GlueContext;
029import com.poscoict.glueframework.context.GlueDefaultContext;
030
031/**
032 * Main 클래스.
033 * 
034 * <pre>
035 * java [-option] com.poscoict.app.job.GlueSimpleJob [args..] 
036 * 형태로 GlueSimpleJob 클래스를 실행한다.
037 * 
038 * [args..] 부분의 첫번째는 history id, 두번째는 실행유형, 세번째부터는 사용자 data로 구성한다. 
039 * <xmp>
040 * java [-option] com.poscoict.app.job.GlueSimpleJob -1 ServiceName=job001-service@S userKey=userData@S
041 * java [-option] com.poscoict.app.job.GlueSimpleJob -1 className=sample.job.SamplePgm@S userKey=userData@S
042 * </xmp>
043 * </pre>
044 */
045public class GlueSimpleJob
046{
047    static ApplicationContext applicationContext = null;
048    static Logger logger = LoggerFactory.getLogger( GlueSimpleJob.class );
049
050    public static void main( String args[] )
051    {
052        logger.info( "GlueSimpleJob version : {}", JobConstants.VERSION );
053        boolean isLocalTest = args != null && args.length > 0 && args[0] != null && "-1".equals( args[0] );
054        if ( !isLocalTest )
055        {
056            BasicDataSource dataSource = null;
057            Connection connection = null;
058            try
059            {
060                applicationContext = new ClassPathXmlApplicationContext( "applicationContext-job.xml" );
061                dataSource = applicationContext.getBean( "dataSource", BasicDataSource.class );
062                connection = DataSourceUtils.getConnection( dataSource );
063                String databaseProductName = connection.getMetaData().getDatabaseProductName().toLowerCase();
064                if ( databaseProductName.contains( "oracle" ) )
065                {
066                    dataSource.addConnectionProperty( "v$session.program", "GlueSimpleJob ver." + JobConstants.VERSION );
067                    // dataSource.addConnectionProperty( "v$session.process", "GlueSimpleJob-java" );
068                    // dataSource.addConnectionProperty( "v$session.terminal", "GlueSimpleJob-terminal" );
069                    // dataSource.addConnectionProperty( "v$session.client_info", "GlueSimpleJob-client" );
070                } else if ( databaseProductName.contains( "postgresql" ) || databaseProductName.contains( "enterprisedb" ) )
071                {
072                    dataSource.addConnectionProperty( "ApplicationName", "GlueSimpleJob ver." + JobConstants.VERSION );
073                } else
074                {
075                    logger.warn( "not support adding datasource connection property (application name) - {}", databaseProductName );
076                }
077
078            } catch ( Exception e )
079            {
080                logger.error( "Exception", e );
081                e.printStackTrace();
082                return;
083            } catch ( Throwable t )
084            {
085                logger.error( "Throwable", t );
086                t.printStackTrace();
087                return;
088            } finally
089            {
090                DataSourceUtils.releaseConnection( connection, dataSource );
091            }
092        }
093
094        String name = ManagementFactory.getRuntimeMXBean().getName();
095        String pid = name.substring( 0, name.indexOf( "@" ) );
096        logger.info( "RuntimeMXBean Name : {}, {}", name, pid );
097        if ( logger.isTraceEnabled() )
098        {
099            // JDK 버전에 따라 지원여부 차이발생.
100            if (ManagementFactory.getRuntimeMXBean().isBootClassPathSupported()) {
101                logger.trace( "BootClassPath : {}", ManagementFactory.getRuntimeMXBean().getBootClassPath() );
102            }
103            logger.trace( "ClassPath : {}", ManagementFactory.getRuntimeMXBean().getClassPath() );
104            logger.trace( "LibraryPath : {}", ManagementFactory.getRuntimeMXBean().getLibraryPath() );
105            logger.trace( "ManagementSpecVersion : {}", ManagementFactory.getRuntimeMXBean().getManagementSpecVersion() );
106            logger.trace( "SpecName : {}", ManagementFactory.getRuntimeMXBean().getSpecName() );
107            logger.trace( "SpecVendor : {}", ManagementFactory.getRuntimeMXBean().getSpecVendor() );
108            logger.trace( "SpecVersion : {}", ManagementFactory.getRuntimeMXBean().getSpecVersion() );
109            logger.trace( "StartTime : {}", ManagementFactory.getRuntimeMXBean().getStartTime() );
110            logger.trace( "Uptime : {}", ManagementFactory.getRuntimeMXBean().getUptime() );
111            logger.trace( "VmName : {}", ManagementFactory.getRuntimeMXBean().getVmName() );
112            logger.trace( "VmVendor : {}", ManagementFactory.getRuntimeMXBean().getVmVendor() );
113            logger.trace( "VmVersion : {}", ManagementFactory.getRuntimeMXBean().getVmVersion() );
114            logger.trace( "SystemProperties : {}", ManagementFactory.getRuntimeMXBean().getSystemProperties() );
115            logger.trace( "BootClassPathSupported : {}", ManagementFactory.getRuntimeMXBean().isBootClassPathSupported() );
116            logger.trace( "InputArguments : {}", ManagementFactory.getRuntimeMXBean().getInputArguments() );
117            List<String> arguements = ManagementFactory.getRuntimeMXBean().getInputArguments();
118            for ( String string : arguements )
119            {
120                logger.trace( "InputArgument : {}", string );
121            }
122        }
123        if ( !isLocalTest )
124            GlueSimpleJob.updateStartTime( args[0], pid );
125        long start = System.currentTimeMillis();
126        GlueContext ctx = new GlueDefaultContext();
127        String requestId = null;
128        try
129        {
130            requestId = args[0];
131            if ( logger.isTraceEnabled() )
132            {
133                Map<String, String> env = System.getenv();
134                logger.trace( "{}", env );
135                logger.trace( "" );
136                logger.trace( "{}", System.getProperties() );
137                logger.trace( "" );
138                logger.trace( "" );
139            }
140
141            logger.info( "RequestID:[{}] StartTime[{}]", requestId, new Date( start ) );
142
143            ctx.put( JobConstants.JOB_REQUEST_ID, requestId );
144
145            for ( int i = 1; i < args.length; i++ )
146            {
147                String arg = args[i];
148                logger.trace( "{}", arg );
149                // K:Keyword;S:String;B:boolean;I:Integer;L:Long;D:Double;F:Float
150                if ( arg.contains( "=" ) )
151                {
152                    String s[] = arg.split( "=" );
153                    if ( arg.startsWith( "\"" ) && arg.endsWith( "\"" ) )
154                    {
155                        String tmp = arg.substring( 1, arg.length() - 1 );
156                        logger.trace( "{}", tmp );
157                        s = tmp.split( "=" );
158                    }
159                    if ( s[1].contains( "@" ) )
160                    {
161                        int idx = s[1].lastIndexOf( "@" );
162                        String value = s[1].substring( 0, idx );
163                        if ( s[1].endsWith( "S" ) )
164                        {
165                            ctx.put( s[0], "NULL".equalsIgnoreCase( value ) ? "" : value );
166                        } else if ( s[1].endsWith( "B" ) )
167                        {
168                            ctx.put( s[0], "NULL".equalsIgnoreCase( value ) ? null : Boolean.valueOf( value ) );
169                        } else if ( s[1].endsWith( "I" ) )
170                        {
171                            ctx.put( s[0], "NULL".equalsIgnoreCase( value ) ? null : new Integer( value ) );
172                        } else if ( s[1].endsWith( "L" ) )
173                        {
174                            ctx.put( s[0], "NULL".equalsIgnoreCase( value ) ? null : new Long( value ) );
175                        } else if ( s[1].endsWith( "D" ) )
176                        {
177                            ctx.put( s[0], "NULL".equalsIgnoreCase( value ) ? null : new Double( value ) );
178                        } else if ( s[1].endsWith( "F" ) )
179                        {
180                            ctx.put( s[0], "NULL".equalsIgnoreCase( value ) ? null : new Float( value ) );
181                        }
182                    } else
183                    {
184                        ctx.put( s[0], s[1] );
185                    }
186                } else
187                {
188                    logger.error( "ignore {}", arg );
189                }
190            }
191            logger.info( "{}", ctx );
192
193            if ( ctx.containsKey( GlueBizControlConstants.SERVICE_NAME ) )
194            {
195                try
196                {
197                    GlueBizProvider.getController().doAction( ctx );
198                } catch ( Throwable t )
199                {
200                    if ( ctx.getException() != null )
201                    {
202                        // BeanCreationException 등 GlueService 실행 전에 발생된 에러를 처리하기 위함.
203                        ctx.setException( t );
204                    } else
205                    {
206                        logger.error( "unknwon error", t );
207                    }
208                    t.printStackTrace();
209                }
210            } else if ( ctx.containsKey( "className" ) )
211            {
212                try
213                {
214                    Object clz = Thread.currentThread().getContextClassLoader().loadClass( (String) ctx.get( "className" ) ).newInstance();
215                    Method method = clz.getClass().getMethod( "runProgram", new Class[] { GlueContext.class } );
216                    method.invoke( clz, new Object[] { ctx } );
217                    if ( ctx.getException() != null )
218                    {
219                        logger.error( "UserException", ctx.getException() );
220                        ctx.getException().printStackTrace();
221                    }
222                    // ctx.getException();
223                } catch ( InstantiationException e )
224                {
225                    ctx.setException( e );
226                    logger.error( "InstantiationException", e );
227                } catch ( IllegalAccessException e )
228                {
229                    ctx.setException( e );
230                    logger.error( "IllegalAccessException", e );
231                } catch ( ClassNotFoundException e )
232                {
233                    ctx.setException( e );
234                    logger.error( "ClassNotFoundException", e );
235                } catch ( SecurityException e )
236                {
237                    ctx.setException( e );
238                    logger.error( "SecurityException", e );
239                } catch ( NoSuchMethodException e )
240                {
241                    ctx.setException( e );
242                    logger.error( "NoSuchMethodException", e );
243                } catch ( IllegalArgumentException e )
244                {
245                    ctx.setException( e );
246                    logger.error( "IllegalArgumentException", e );
247                } catch ( InvocationTargetException e )
248                {
249                    ctx.setException( e );
250                    logger.error( "InvocationTargetException", e );
251                } catch ( Throwable t )
252                {
253                    ctx.setException( t );
254                    logger.error( "UserException", t );
255                }
256            } else
257            {
258                ctx.setException( new Exception( "Service is Null!" ) );
259                logger.info( "Service is Null!" );
260            }
261
262        } catch ( Exception e )
263        {
264            logger.error( "Exception", e );
265            e.printStackTrace();
266        } catch ( Throwable t )
267        {
268            logger.error( "Throwable", t );
269            t.printStackTrace();
270        } finally
271        {
272            long end = System.currentTimeMillis();
273            if ( !isLocalTest )
274                GlueSimpleJob.updateEndTime( args[0], end - start, ctx.getException() );
275            logger.info( "RequestID:[{}] EndTime[{}] RunTime:[{}]", requestId, new Date( end ), end - start );
276        }
277        System.exit( 0 );
278    }
279
280    private static void updateEndTime( String id, long runtime, Throwable throwable )
281    {
282        try
283        {
284            if ( "-1".equals( id ) )
285            {
286                return;
287            }
288            DataSource ds = applicationContext.getBean( "dataSource", DataSource.class );
289            logger.debug( "{}", JobConstants.SQL_PGM_END );
290            logger.trace( "{}", runtime );
291            logger.trace( "{}", throwable == null ? "END" : "ERROR" );
292            logger.trace( "{}", id );
293            Connection con = null;
294            PreparedStatement ps = null;
295            con = DataSourceUtils.getConnection( ds );
296            try
297            {
298                ps = con.prepareStatement( JobConstants.SQL_PGM_END );
299                ps.setObject( 1, runtime );
300                ps.setObject( 2, throwable == null ? "END" : "ERROR" );
301                ps.setObject( 3, new Long( id ) );
302                ps.executeUpdate();
303                con.commit();
304            } catch ( SQLException e )
305            {
306                logger.error( "fail to executeUpdate", e );
307            } finally
308            {
309                JdbcUtils.closeStatement( ps );
310                DataSourceUtils.releaseConnection( con, ds );
311            }
312        } catch ( Exception e )
313        {
314            logger.error( "fail to getConnection", e );
315        } catch ( Throwable t )
316        {
317            logger.error( "fail to getConnection", t );
318        }
319    }
320
321    private static void updateStartTime( String id, String pid )
322    {
323        try
324        {
325            if ( "-1".equals( id ) )
326            {
327                return;
328            }
329            DataSource ds = applicationContext.getBean( "dataSource", DataSource.class );
330            logger.debug( "{}", JobConstants.SQL_PGM_START );
331            logger.trace( "{}", "RUNNING" );
332            logger.trace( "{}", pid );
333            logger.trace( "{}", id );
334
335            Connection con = null;
336            PreparedStatement ps = null;
337            con = DataSourceUtils.getConnection( ds );
338            try
339            {
340                ps = con.prepareStatement( JobConstants.SQL_PGM_START );
341                ps.setObject( 1, "RUNNING" );
342                ps.setObject( 2, new Integer( pid ) );
343                ps.setObject( 3, new Long( id ) );
344                ps.executeUpdate();
345                con.commit();
346            } catch ( SQLException e )
347            {
348                logger.error( "fail to executeUpdate", e );
349            } catch ( Exception e )
350            {
351                logger.error( "fail to getConnection", e );
352            } catch ( Throwable t )
353            {
354                logger.error( "fail to getConnection", t );
355            } finally
356            {
357                JdbcUtils.closeStatement( ps );
358                DataSourceUtils.releaseConnection( con, ds );
359            }
360        } catch ( Exception e )
361        {
362            logger.error( "fail to getConnection", e );
363        } catch ( Throwable t )
364        {
365            logger.error( "fail to getConnection", t );
366        }
367    }
368}