/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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.apache.zeppelin.ignite; import org.apache.zeppelin.interpreter.Interpreter; import org.apache.zeppelin.interpreter.InterpreterContext; import org.apache.zeppelin.interpreter.InterpreterException; import org.apache.zeppelin.interpreter.InterpreterPropertyBuilder; import org.apache.zeppelin.interpreter.InterpreterResult; import org.apache.zeppelin.interpreter.InterpreterResult.Code; import org.apache.zeppelin.scheduler.Scheduler; import org.apache.zeppelin.scheduler.SchedulerFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.Statement; import java.sql.SQLException; import java.util.LinkedList; import java.util.List; import java.util.Properties; /** * Apache Ignite SQL interpreter (http://ignite.incubator.apache.org/). * * Use {@code ignite.jdbc.url} property to set up JDBC connection URL. * URL has the following pattern: * {@code jdbc:ignite://<hostname>:<port>/<cache_name>} * * <ul> * <li>Hostname is required.</li> * <li>If port is not defined, 11211 is used (default for Ignite client).</li> * <li>Leave cache_name empty if you are connecting to a default cache. * Note that the cache name is case sensitive.</li> * </ul> */ public class IgniteSqlInterpreter extends Interpreter { private static final String IGNITE_JDBC_DRIVER_NAME = "org.apache.ignite.IgniteJdbcDriver"; static final String IGNITE_JDBC_URL = "ignite.jdbc.url"; static { Interpreter.register( "ignitesql", "ignite", IgniteSqlInterpreter.class.getName(), new InterpreterPropertyBuilder() .add(IGNITE_JDBC_URL, "jdbc:ignite:cfg://default-ignite-jdbc.xml", "Ignite JDBC connection URL.") .build()); } private Logger logger = LoggerFactory.getLogger(IgniteSqlInterpreter.class); private Connection conn; private Throwable connEx; private Statement curStmt; public IgniteSqlInterpreter(Properties property) { super(property); } @Override public void open() { try { Class.forName(IGNITE_JDBC_DRIVER_NAME); } catch (ClassNotFoundException e) { logger.error("Can't open connection", e); connEx = e; return; } try { logger.info("connect to " + getProperty(IGNITE_JDBC_URL)); conn = DriverManager.getConnection(getProperty(IGNITE_JDBC_URL)); connEx = null; logger.info("Successfully created JDBC connection"); } catch (SQLException e) { logger.error("Can't open connection: ", e); connEx = e; } } @Override public void close() { try { if (conn != null) { conn.close(); } } catch (SQLException e) { throw new InterpreterException(e); } finally { conn = null; connEx = null; } } @Override public InterpreterResult interpret(String st, InterpreterContext context) { if (connEx != null) { return new InterpreterResult(Code.ERROR, connEx.getMessage()); } StringBuilder msg = new StringBuilder("%table "); try (Statement stmt = conn.createStatement()) { curStmt = stmt; try (ResultSet res = stmt.executeQuery(st)) { ResultSetMetaData md = res.getMetaData(); for (int i = 1; i <= md.getColumnCount(); i++) { if (i > 1) { msg.append('\t'); } msg.append(md.getColumnName(i)); } msg.append('\n'); while (res.next()) { for (int i = 1; i <= md.getColumnCount(); i++) { msg.append(res.getString(i)); if (i != md.getColumnCount()) { msg.append('\t'); } } msg.append('\n'); } } } catch (Exception e) { return IgniteInterpreterUtils.buildErrorResult(e); } finally { curStmt = null; } return new InterpreterResult(Code.SUCCESS, msg.toString()); } @Override public void cancel(InterpreterContext context) { if (curStmt != null) { try { curStmt.cancel(); } catch (SQLException e) { // No-op. } finally { curStmt = null; } } } @Override public FormType getFormType() { return FormType.SIMPLE; } @Override public int getProgress(InterpreterContext context) { return 0; } @Override public Scheduler getScheduler() { return SchedulerFactory.singleton().createOrGetFIFOScheduler( IgniteSqlInterpreter.class.getName() + this.hashCode()); } @Override public List<String> completion(String buf, int cursor) { return new LinkedList<>(); } }