├── .checkstyle ├── .classpath ├── .gitignore ├── .project ├── .settings ├── org.eclipse.core.resources.prefs ├── org.eclipse.jdt.core.prefs └── org.eclipse.m2e.core.prefs ├── LICENSE ├── README.md ├── pom.xml └── src ├── it └── java │ └── info │ └── michaelwittig │ └── javaq │ └── connector │ └── impl │ ├── ATest.java │ ├── ATestWithConnection.java │ ├── ATestWithConnectionSync.java │ ├── QConnectorAsyncImplReconnectTest.java │ ├── QConnectorSyncImplReconnectTest.java │ ├── QConnectorSyncImplTest.java │ ├── QConnectorSyncTSImplReconnectTest.java │ └── QConnectorSyncTSImplTest.java ├── main └── java │ ├── info │ └── michaelwittig │ │ └── javaq │ │ ├── Builder.java │ │ ├── Q.java │ │ ├── connector │ │ ├── QConnector.java │ │ ├── QConnectorAsync.java │ │ ├── QConnectorDataListener.java │ │ ├── QConnectorError.java │ │ ├── QConnectorException.java │ │ ├── QConnectorListener.java │ │ ├── QConnectorSync.java │ │ └── impl │ │ │ ├── CResultHelper.java │ │ │ ├── QConnectorAsyncImpl.java │ │ │ ├── QConnectorFactory.java │ │ │ ├── QConnectorImpl.java │ │ │ ├── QConnectorSyncImpl.java │ │ │ ├── QConnectorSyncTSImpl.java │ │ │ └── cmd │ │ │ ├── AConnectorSyncCommand.java │ │ │ ├── ConnectorAsyncCommand.java │ │ │ ├── ConnectorAsyncCommandQ.java │ │ │ ├── ConnectorSyncCommand.java │ │ │ ├── ConnectorSyncCommandFunction.java │ │ │ ├── ConnectorSyncCommandQ.java │ │ │ └── ConnectorSyncCommandSelect.java │ │ └── query │ │ ├── ATableResult.java │ │ ├── EmptyResult.java │ │ ├── FlipFlipResult.java │ │ ├── FlipResult.java │ │ ├── Function.java │ │ ├── ListResult.java │ │ ├── PrimitiveResult.java │ │ ├── Result.java │ │ ├── Select.java │ │ ├── Table.java │ │ ├── TableResult.java │ │ ├── TableRow.java │ │ ├── column │ │ ├── AggregateColumn.java │ │ ├── AggregatingNominal.java │ │ ├── AggregatingOrdinal.java │ │ ├── Aggregation.java │ │ ├── Column.java │ │ ├── VirtualColumn.java │ │ ├── Virtualling.java │ │ └── impl │ │ │ ├── ASimpleNominalColumn.java │ │ │ ├── ASimpleOrdinalColumn.java │ │ │ ├── AggregateColumnImpl.java │ │ │ ├── BooleanColumn.java │ │ │ ├── DateColumn.java │ │ │ ├── DateTimeColumn.java │ │ │ ├── FloatColumn.java │ │ │ ├── IntegerColumn.java │ │ │ ├── LongColumn.java │ │ │ ├── RealColumn.java │ │ │ ├── SymbolColumn.java │ │ │ ├── TimeColumn.java │ │ │ ├── TimestampColumn.java │ │ │ └── VirtualColumnImpl.java │ │ ├── filter │ │ ├── ComparisonFiltering.java │ │ ├── EqualityFiltering.java │ │ ├── Filter.java │ │ └── impl │ │ │ ├── FilterComparator.java │ │ │ └── FilterImpl.java │ │ ├── group │ │ ├── Group.java │ │ ├── Grouping.java │ │ ├── XbarGrouping.java │ │ └── impl │ │ │ └── XbarGroupImpl.java │ │ ├── impl │ │ ├── ATable.java │ │ ├── FunctionImpl.java │ │ ├── SelectImpl.java │ │ ├── TableImpl.java │ │ ├── TableIterator.java │ │ └── TableRowImpl.java │ │ ├── type │ │ ├── List.java │ │ ├── NominalType.java │ │ ├── OrdinalType.java │ │ ├── Type.java │ │ ├── ValueFactory.java │ │ └── impl │ │ │ ├── TypeBoolean.java │ │ │ ├── TypeDate.java │ │ │ ├── TypeDateTime.java │ │ │ ├── TypeFloat.java │ │ │ ├── TypeInteger.java │ │ │ ├── TypeList.java │ │ │ ├── TypeLong.java │ │ │ ├── TypeReal.java │ │ │ ├── TypeSymbol.java │ │ │ ├── TypeTime.java │ │ │ └── TypeTimestamp.java │ │ └── value │ │ ├── Value.java │ │ └── impl │ │ ├── AValue.java │ │ ├── BooleanValue.java │ │ ├── DateTimeValue.java │ │ ├── DateValue.java │ │ ├── FloatValue.java │ │ ├── IntegerValue.java │ │ ├── ListValue.java │ │ ├── LongValue.java │ │ ├── RealValue.java │ │ ├── SymbolValue.java │ │ ├── TimeValue.java │ │ └── TimestampValue.java │ └── kx │ └── c.java └── test └── java └── info └── michaelwittig └── javaq ├── demo ├── ConsoleSubscriber.java ├── ReconnectTest.java ├── SelectQueryExample.java └── tabledefinition │ ├── MyTable.java │ └── SelectQueryExample.java └── query ├── column └── impl │ ├── ASimpleNominalColumnTest.java │ └── ASimpleOrdinalColumnTest.java ├── filter └── impl │ └── FilterImplTest.java ├── impl ├── FunctionImplTest.java ├── SelectImplTest.java └── TableImplTest.java └── value └── impl ├── BooleanValueTest.java ├── FloatValueTest.java ├── IntegerValueTest.java ├── LongValueTest.java ├── RealValueTest.java ├── SymbolValueTest.java ├── TimeValueTest.java └── TimestampValueTest.java /.checkstyle: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | .idea/ 3 | *.iml -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | java-q 4 | 5 | 6 | 7 | 8 | 9 | org.eclipse.jdt.core.javabuilder 10 | 11 | 12 | 13 | 14 | net.sf.eclipsecs.core.CheckstyleBuilder 15 | 16 | 17 | 18 | 19 | org.eclipse.m2e.core.maven2Builder 20 | 21 | 22 | 23 | 24 | 25 | org.eclipse.m2e.core.maven2Nature 26 | org.eclipse.jdt.core.javanature 27 | net.sf.eclipsecs.core.CheckstyleNature 28 | 29 | 30 | -------------------------------------------------------------------------------- /.settings/org.eclipse.core.resources.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | encoding//src/main/java=UTF-8 3 | encoding//src/test/java=UTF-8 4 | encoding/=UTF-8 5 | -------------------------------------------------------------------------------- /.settings/org.eclipse.jdt.core.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 3 | org.eclipse.jdt.core.compiler.compliance=1.6 4 | org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning 5 | org.eclipse.jdt.core.compiler.source=1.6 6 | -------------------------------------------------------------------------------- /.settings/org.eclipse.m2e.core.prefs: -------------------------------------------------------------------------------- 1 | #Wed Mar 21 08:36:35 CET 2012 2 | activeProfiles= 3 | eclipse.preferences.version=1 4 | resolveWorkspaceProjects=true 5 | version=1 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://secure.travis-ci.org/michaelwittig/java-q.png)](http://travis-ci.org/michaelwittig/java-q) 2 | 3 | # java-q 4 | 5 | Hej folks! Again and again we have to interact with a [kdb+](http://kx.com/kdb+.php) and [kdb+tick](http://kx.com/kdb+tick.php). The guys from [KX Systems](http://kx.com/) provide us [c.java](http://code.kx.com/wsvn/code/kx/kdb%2B/c/kx/c.java) to interact with their product. You might experienced that c.java is not really using an object oriented style (because q does not?), so we started to write our own wrapper and called it java-q. Feel free to create [Issues](https://github.com/michaelwittig/java-q/issues) if something is missing for you. And now: have fun:) 6 | 7 | ## Encapsulation 8 | 9 | **You do not need to be a q god to use this library!** 10 | 11 | We encapsulate all the q stuff for you! It's just simple Java. Look at the following example. Have you ever seen a [q-sql select](http://code.kx.com/wiki/JB:QforMortals/queries_q_sql) so Java pretty? 12 | 13 | ```java 14 | Select select = trade.select() 15 | .column(size.sum()) 16 | .column(price.avg()) 17 | .group(sym.group()) 18 | .group(time.xbar(LongValue.from(60000L))) 19 | .filter(sym.filterIn(SymbolValue.froms(new String[] {"AAA", "BBB"}))) 20 | .order(Direction.descending, time) 21 | .build(); 22 | ``` 23 | 24 | ## Schema definition 25 | 26 | Your schema is defined by code not by text! You get easy refactoring and lesser typos for free:) 27 | 28 | [Learn more about how to define your schema with a few lines of code.](https://github.com/michaelwittig/java-q/wiki/HowTo:-Schema) 29 | 30 | ```java 31 | public class MyTable extends ATable { 32 | private static MyTable INSTANCE = new MyTable(); 33 | public static MyTable get() { 34 | return INSTANCE; 35 | } 36 | 37 | public TimeColumn time = new TimeColumn("time"); 38 | public SymbolColumn sym = new SymbolColumn("sym"); 39 | public FloatColumn price = new FloatColumn("price"); 40 | public IntegerColumn size = new IntegerColumn("size"); 41 | 42 | private MyTable() { 43 | super("mytable"); 44 | this.addColumn(this.time); 45 | this.addColumn(this.sym); 46 | this.addColumn(this.price); 47 | this.addColumn(this.size); 48 | } 49 | } 50 | ``` 51 | 52 | ## Synchronous Access 53 | 54 | ### Queries using select 55 | 56 | [Learn more about how to create a q query.](https://github.com/michaelwittig/java-q/wiki/HowTo:-Query) 57 | 58 | ```java 59 | // get table schema 60 | MyTable table = MyTable.get(); 61 | 62 | // create select query 63 | Select select = table.select() 64 | .column(table.size.sum()) 65 | .column(table.price.avg()) 66 | .group(table.sym.group()) 67 | .filter(table.sym.filterIn(SymbolValue.froms(new String[] {"AAA", "BBB"}))) 68 | .order(Direction.descending, table.time) 69 | .build(); 70 | System.out.println("q: " + select.toQ()); 71 | 72 | // connect to kdb+ 73 | QConnectorSync kx = QConnectorFactory.create("localhost", 5011); 74 | kx.connect(); 75 | 76 | // execute select query and print the result 77 | Result result = kx.select(select); 78 | for (TableRow row : table.read(result)) { 79 | System.out.println(row.get(table.time)); 80 | System.out.println(row.get(table.sym)); 81 | System.out.println(row.get(table.price)); 82 | System.out.println(row.get(table.size)); 83 | } 84 | 85 | // close connection 86 | kx.disconnect(); 87 | ``` 88 | 89 | ## Asynchronous Access 90 | 91 | ### Subscription using .u.sub[] 92 | 93 | [Learn more about how to subscribe to q kdb+tick environement.](https://github.com/michaelwittig/java-q/wiki/HowTo:-Subscription) 94 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | 4 | org.sonatype.oss 5 | oss-parent 6 | 9 7 | 8 | info.michaelwittig 9 | java-q 10 | 1.0.1-SNAPSHOT 11 | Q interfacing with Java 12 | ... 13 | https://github.com/michaelwittig/java-q 14 | 2012 15 | 16 | 17 | Apache License, Version 2.0 18 | http://www.apache.org/licenses/LICENSE-2.0.txt 19 | repo 20 | 21 | 22 | 23 | scm:git:git@github.com:michaelwittig/java-q.git 24 | scm:git:git@github.com:michaelwittig/java-q.git 25 | git@github.com:michaelwittig/java-q.git 26 | 27 | 28 | github 29 | https://github.com/michaelwittig/java-q/issues 30 | 31 | 32 | UTF-8 33 | yyyyMMdd-HHmmss 34 | 35 | 36 | 37 | mwittig 38 | post@michaelwittig.info 39 | Michael Wittig 40 | 41 | 42 | thoeger 43 | thorsten.hoeger@hoegernet.com 44 | Thorsten Hoeger 45 | 46 | 47 | 48 | 49 | 50 | org.apache.commons 51 | commons-lang3 52 | 3.3.2 53 | 54 | 55 | com.google.guava 56 | guava 57 | 18.0 58 | 59 | 60 | net.java.dev.jna 61 | jna 62 | 4.1.0 63 | test 64 | 65 | 66 | net.java.dev.jna 67 | platform 68 | 3.5.2 69 | test 70 | 71 | 72 | junit 73 | junit 74 | 4.11 75 | test 76 | 77 | 78 | 79 | 80 | 81 | org.apache.maven.plugins 82 | maven-compiler-plugin 83 | 84 | 1.7 85 | 1.7 86 | true 87 | 88 | 89 | 90 | 91 | 92 | 3.0.0 93 | 94 | 95 | -------------------------------------------------------------------------------- /src/it/java/info/michaelwittig/javaq/connector/impl/ATest.java: -------------------------------------------------------------------------------- 1 | package info.michaelwittig.javaq.connector.impl; 2 | 3 | import java.lang.reflect.Field; 4 | import java.util.HashMap; 5 | import java.util.Map; 6 | 7 | import com.sun.jna.Pointer; 8 | import com.sun.jna.platform.win32.Kernel32; 9 | import com.sun.jna.platform.win32.WinNT; 10 | import org.apache.commons.lang3.SystemUtils; 11 | 12 | /** 13 | * Assumes the ENV QHOME to be set! 14 | * 15 | * @author mwittig 16 | * @author bdupreez.. added windows support 17 | */ 18 | public abstract class ATest { 19 | 20 | private static final Map port2pid = new HashMap(); 21 | private static final String DEFAULT_QHOME = "C:/q"; 22 | 23 | protected void launchQProcess() throws Exception { 24 | this.launchQProcess(5000); 25 | } 26 | 27 | protected synchronized void launchQProcess(final int port) throws Exception { 28 | String qhome = System.getenv("QHOME"); 29 | if (qhome == null) { 30 | qhome = DEFAULT_QHOME; 31 | } 32 | if (ATest.port2pid.containsKey(port)) { 33 | throw new RuntimeException("port already used"); 34 | } 35 | final String path; 36 | if (SystemUtils.IS_OS_LINUX) { 37 | path = qhome + "/l32/q"; 38 | } else if (SystemUtils.IS_OS_MAC) { 39 | path = qhome + "/m32/q"; 40 | } else if (SystemUtils.IS_OS_WINDOWS) { 41 | path = qhome + "/w32/q"; 42 | } else { 43 | throw new RuntimeException("OS not supported!"); 44 | } 45 | final Process p = new ProcessBuilder(path, "-p", String.valueOf(port)).start(); 46 | final int pid; 47 | try { 48 | if (SystemUtils.IS_OS_WINDOWS) { 49 | pid = determineWindowsProcessId(p); 50 | } else { 51 | pid = getLinuxMacPid(p); 52 | } 53 | } catch (final Exception e) { 54 | throw new RuntimeException("OS related exception!", e); 55 | } 56 | ATest.port2pid.put(port, pid); 57 | Thread.sleep(200); // wait until started 58 | } 59 | 60 | private int getLinuxMacPid(final Process p) throws NoSuchFieldException, IllegalAccessException { 61 | final Field f = p.getClass().getDeclaredField("pid"); 62 | f.setAccessible(true); 63 | return f.getInt(p); 64 | } 65 | 66 | protected void terminateQProcess() throws Exception { 67 | this.terminateQProcess(5000); 68 | } 69 | 70 | protected void terminateQProcess(final int port) throws Exception { 71 | final Integer pid = ATest.port2pid.get(port); 72 | ATest.port2pid.remove(port); 73 | if (pid == null) { 74 | throw new RuntimeException("port not used"); 75 | } 76 | if (SystemUtils.IS_OS_WINDOWS) { 77 | new ProcessBuilder("taskkill", "/PID", pid.toString(), "/T", "/F").start().waitFor(); 78 | }else { 79 | new ProcessBuilder("kill", "-9", pid.toString()).start().waitFor(); 80 | } 81 | } 82 | 83 | private int determineWindowsProcessId(final Process proc) { 84 | if (proc.getClass().getName().equals("java.lang.Win32Process") 85 | || proc.getClass().getName().equals("java.lang.ProcessImpl")) { 86 | try { 87 | final Field f = proc.getClass().getDeclaredField("handle"); 88 | f.setAccessible(true); 89 | final long handleId = f.getLong(proc); 90 | final Kernel32 kernel = Kernel32.INSTANCE; 91 | final WinNT.HANDLE handle = new WinNT.HANDLE(); 92 | handle.setPointer(Pointer.createConstant(handleId)); 93 | return kernel.GetProcessId(handle); 94 | } catch (final Throwable e) { 95 | throw new RuntimeException(e.getMessage()); 96 | } 97 | } 98 | return 0; 99 | } 100 | 101 | } 102 | -------------------------------------------------------------------------------- /src/it/java/info/michaelwittig/javaq/connector/impl/ATestWithConnection.java: -------------------------------------------------------------------------------- 1 | package info.michaelwittig.javaq.connector.impl; 2 | 3 | import info.michaelwittig.javaq.connector.QConnector; 4 | 5 | import org.junit.After; 6 | import org.junit.Before; 7 | 8 | /** 9 | * Assumes running q process on localhost:5000! 10 | * 11 | * @author mwittig 12 | * 13 | * @param QConnector 14 | * 15 | */ 16 | @SuppressWarnings("javadoc") 17 | public abstract class ATestWithConnection extends ATest { 18 | 19 | private volatile T c; 20 | 21 | 22 | abstract T create(String host, int port); 23 | 24 | @Before 25 | public void setUp() throws Exception { 26 | this.launchQProcess(); 27 | this.c = this.create("localhost", 5000); 28 | this.c.connect(); 29 | } 30 | 31 | @After 32 | public void tearDOwn() throws Exception { 33 | this.terminateQProcess(); 34 | } 35 | 36 | protected T c() { 37 | return this.c; 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/it/java/info/michaelwittig/javaq/connector/impl/QConnectorAsyncImplReconnectTest.java: -------------------------------------------------------------------------------- 1 | package info.michaelwittig.javaq.connector.impl; 2 | 3 | import info.michaelwittig.javaq.connector.QConnectorError; 4 | import info.michaelwittig.javaq.connector.QConnectorException; 5 | import info.michaelwittig.javaq.connector.QConnectorListener; 6 | import info.michaelwittig.javaq.query.Result; 7 | 8 | import org.junit.Assert; 9 | import org.junit.Test; 10 | 11 | /** 12 | * @author mwittig 13 | * 14 | */ 15 | @SuppressWarnings("javadoc") 16 | public final class QConnectorAsyncImplReconnectTest extends ATest { 17 | 18 | @Test 19 | public void testConnectionFail() throws Exception { 20 | this.launchQProcess(); 21 | final QConnectorAsyncImpl c = new QConnectorAsyncImpl(new QConnectorListener() { 22 | 23 | @Override 24 | public void error(QConnectorError e) { 25 | // reports about reconnects 26 | } 27 | 28 | @Override 29 | public void exception(QConnectorException e) { 30 | // reports about exceptions 31 | } 32 | 33 | @Override 34 | public void resultReceived(final String handle, final Result result) { 35 | // data received 36 | } 37 | }, "localhost", 5000, false); 38 | c.connect(); 39 | c.subscribe("test", new String[] {"test"}, new String[] {"TEST"}); 40 | Thread.sleep(1500); 41 | this.terminateQProcess(); 42 | Thread.sleep(1500); 43 | try { 44 | c.disconnect(); 45 | Assert.fail("should fail because disconnect is already triggert"); 46 | } catch (final Exception e) { 47 | // nothing 48 | } 49 | this.launchQProcess(); 50 | c.connect(); 51 | Thread.sleep(500); 52 | c.disconnect(); 53 | this.terminateQProcess(); 54 | } 55 | 56 | @Test() 57 | public void testConnectionFailWithReconnect() throws Exception { 58 | this.launchQProcess(); 59 | final QConnectorAsyncImpl c = new QConnectorAsyncImpl(new QConnectorListener() { 60 | 61 | @Override 62 | public void error(QConnectorError e) { 63 | // reports about reconnects 64 | } 65 | 66 | @Override 67 | public void exception(QConnectorException e) { 68 | throw new RuntimeException(e); 69 | } 70 | 71 | @Override 72 | public void resultReceived(final String handle, final Result result) { 73 | // data received 74 | } 75 | }, "localhost", 5000, true); 76 | c.connect(); 77 | c.subscribe("test", new String[] {"test"}, new String[] {"TEST"}); 78 | Thread.sleep(1500); 79 | this.terminateQProcess(); 80 | Thread.sleep(3000); 81 | this.launchQProcess(); 82 | Thread.sleep(1500); 83 | c.disconnect(); 84 | this.terminateQProcess(); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/it/java/info/michaelwittig/javaq/connector/impl/QConnectorSyncImplReconnectTest.java: -------------------------------------------------------------------------------- 1 | package info.michaelwittig.javaq.connector.impl; 2 | 3 | import info.michaelwittig.javaq.query.PrimitiveResult; 4 | import info.michaelwittig.javaq.query.Result; 5 | 6 | import org.junit.Assert; 7 | import org.junit.Test; 8 | 9 | /** 10 | * @author mwittig 11 | * 12 | */ 13 | @SuppressWarnings("javadoc") 14 | public final class QConnectorSyncImplReconnectTest extends ATest { 15 | 16 | @Test 17 | public void testConnectionFail() throws Exception { 18 | this.launchQProcess(); 19 | final QConnectorSyncImpl c = new QConnectorSyncImpl("localhost", 5000, false); 20 | c.connect(); 21 | Result r1 = c.execute("1+1"); 22 | Assert.assertTrue(r1 instanceof PrimitiveResult); 23 | this.terminateQProcess(); 24 | Thread.sleep(1000); 25 | try { 26 | c.execute("1+1"); 27 | Assert.fail("Should fail"); 28 | } catch (final Exception e) { 29 | e.printStackTrace(); 30 | } 31 | Thread.sleep(1000); 32 | this.launchQProcess(); 33 | try { 34 | c.execute("1+1"); 35 | Assert.fail("Should fail"); 36 | } catch (final Exception e) { 37 | e.printStackTrace(); 38 | } finally { 39 | this.terminateQProcess(); 40 | } 41 | } 42 | 43 | @Test 44 | public void testConnectionFailWithReconnect() throws Exception { 45 | this.launchQProcess(); 46 | final QConnectorSyncImpl c = new QConnectorSyncImpl("localhost", 5000, true); 47 | c.connect(); 48 | Result r1 = c.execute("1+1"); 49 | Assert.assertTrue(r1 instanceof PrimitiveResult); 50 | this.terminateQProcess(); 51 | Thread.sleep(1000); 52 | try { 53 | c.execute("1+1"); 54 | Assert.fail("Should fail"); 55 | } catch (final Exception e) { 56 | e.printStackTrace(); 57 | } 58 | Thread.sleep(1000); 59 | this.launchQProcess(); 60 | Result r2 = c.execute("1+1"); 61 | Assert.assertTrue(r2 instanceof PrimitiveResult); 62 | this.terminateQProcess(); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/it/java/info/michaelwittig/javaq/connector/impl/QConnectorSyncImplTest.java: -------------------------------------------------------------------------------- 1 | package info.michaelwittig.javaq.connector.impl; 2 | 3 | /** 4 | * @author mwittig 5 | * 6 | */ 7 | public final class QConnectorSyncImplTest extends ATestWithConnectionSync { 8 | 9 | @Override 10 | QConnectorSyncImpl create(final String host, final int port) { 11 | return new QConnectorSyncImpl(host, port, true); 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /src/it/java/info/michaelwittig/javaq/connector/impl/QConnectorSyncTSImplReconnectTest.java: -------------------------------------------------------------------------------- 1 | package info.michaelwittig.javaq.connector.impl; 2 | 3 | import info.michaelwittig.javaq.query.PrimitiveResult; 4 | import info.michaelwittig.javaq.query.Result; 5 | import info.michaelwittig.javaq.query.TableResult; 6 | 7 | import org.junit.Assert; 8 | import org.junit.Test; 9 | 10 | /** 11 | * @author mwittig 12 | * 13 | */ 14 | @SuppressWarnings("javadoc") 15 | public final class QConnectorSyncTSImplReconnectTest extends ATest { 16 | 17 | @Test 18 | public void testConnectionFail() throws Exception { 19 | this.launchQProcess(); 20 | final QConnectorSyncTSImpl c = new QConnectorSyncTSImpl("localhost", 5000, false); 21 | c.connect(); 22 | Result r1 = c.execute("1+1"); 23 | Assert.assertTrue(r1 instanceof PrimitiveResult); 24 | this.terminateQProcess(); 25 | Thread.sleep(1000); 26 | try { 27 | c.execute("1+1"); 28 | Assert.fail("Should fail"); 29 | } catch (final Exception e) { 30 | e.printStackTrace(); 31 | } 32 | Thread.sleep(1000); 33 | this.launchQProcess(); 34 | try { 35 | c.execute("1+1"); 36 | Assert.fail("Should fail"); 37 | } catch (final Exception e) { 38 | e.printStackTrace(); 39 | } finally { 40 | this.terminateQProcess(); 41 | } 42 | } 43 | 44 | @Test 45 | public void testConnectionFailWithReconnect() throws Exception { 46 | this.launchQProcess(); 47 | final QConnectorSyncTSImpl c = new QConnectorSyncTSImpl("localhost", 5000, true); 48 | c.connect(); 49 | Result r1 = c.execute("1+1"); 50 | Assert.assertTrue(r1 instanceof PrimitiveResult); 51 | this.terminateQProcess(); 52 | Thread.sleep(1000); 53 | try { 54 | c.execute("1+1"); 55 | Assert.fail("Should fail"); 56 | } catch (final Exception e) { 57 | e.printStackTrace(); 58 | } 59 | Thread.sleep(1000); 60 | this.launchQProcess(); 61 | Result r2 = c.execute("1+1"); 62 | Assert.assertTrue(r2 instanceof PrimitiveResult); 63 | this.terminateQProcess(); 64 | } 65 | 66 | @Test 67 | public void testReadTable() throws Exception { 68 | try { 69 | this.launchQProcess(); 70 | final QConnectorSyncTSImpl c = new QConnectorSyncTSImpl("localhost", 5000, true); 71 | c.connect(); 72 | final TableResult res = (TableResult) c.execute("([] a:(1 2 3 4 5 6); b:(111000b); c:(`a`b`c`d`e`f))"); 73 | Assert.assertEquals(3, res.getCols()); 74 | Assert.assertEquals(6, res.getRows()); 75 | Assert.assertArrayEquals(new String[] {"a", "b", "c"}, res.getColNames()); 76 | Assert.assertEquals(1L, res.getAt(0, 0)); 77 | Assert.assertEquals(1L, res.getAt("a", 0)); 78 | } finally { 79 | this.terminateQProcess(); 80 | } 81 | } 82 | 83 | } 84 | -------------------------------------------------------------------------------- /src/it/java/info/michaelwittig/javaq/connector/impl/QConnectorSyncTSImplTest.java: -------------------------------------------------------------------------------- 1 | package info.michaelwittig.javaq.connector.impl; 2 | 3 | /** 4 | * @author mwittig 5 | * 6 | */ 7 | public final class QConnectorSyncTSImplTest extends ATestWithConnectionSync { 8 | 9 | @Override 10 | QConnectorSyncTSImpl create(final String host, final int port) { 11 | return new QConnectorSyncTSImpl(host, port, true); 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/info/michaelwittig/javaq/Builder.java: -------------------------------------------------------------------------------- 1 | package info.michaelwittig.javaq; 2 | 3 | /** 4 | * Builder. 5 | * 6 | * @author mwittig 7 | * 8 | * @param Class 9 | */ 10 | public interface Builder { 11 | 12 | /** 13 | * @return Class 14 | */ 15 | C build(); 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/info/michaelwittig/javaq/Q.java: -------------------------------------------------------------------------------- 1 | package info.michaelwittig.javaq; 2 | 3 | /** 4 | * Represent a q command line. 5 | * 6 | * @author mwittig 7 | * 8 | */ 9 | public interface Q { 10 | 11 | /** 12 | * @return Q 13 | */ 14 | String toQ(); 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/info/michaelwittig/javaq/connector/QConnector.java: -------------------------------------------------------------------------------- 1 | package info.michaelwittig.javaq.connector; 2 | 3 | /** 4 | * QConnector. 5 | * 6 | * Error / Exception handling: Errors are failures that the system can handle automatically and to correct the failure situation. For 7 | * example if the connection is lost and reconnectOnError is true, the QConnector will try to reconnect to the server. Exceptions are 8 | * failures that the system can not handle by itself. For example an malformed q command. 9 | * 10 | * @author mwittig 11 | * 12 | */ 13 | public abstract interface QConnector { 14 | 15 | /** 16 | * Connect. 17 | * 18 | * @throws QConnectorException If the connection can not be established 19 | * @throws QConnectorError If the QConnector is already connected 20 | */ 21 | void connect() throws QConnectorException, QConnectorError; 22 | 23 | /** 24 | * Disconnect. 25 | * 26 | * @throws QConnectorError If the connection was disconnected already 27 | */ 28 | void disconnect() throws QConnectorError; 29 | 30 | /** 31 | * @return Host 32 | */ 33 | String getHost(); 34 | 35 | /** 36 | * @return Port 37 | */ 38 | int getPort(); 39 | 40 | /** 41 | * @return Reconnect on error? 42 | */ 43 | boolean reconnectOnError(); 44 | 45 | /** 46 | * @return Connected? 47 | */ 48 | boolean isConnected(); 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/info/michaelwittig/javaq/connector/QConnectorAsync.java: -------------------------------------------------------------------------------- 1 | package info.michaelwittig.javaq.connector; 2 | 3 | /** 4 | * QConnector asynchronous. 5 | * 6 | * Thread-safe? yes 7 | * 8 | * @author mwittig 9 | * 10 | */ 11 | public interface QConnectorAsync extends QConnector { 12 | 13 | /** 14 | * Subscribe and receive data via global QConnectorListener. 15 | * 16 | * @param handle Unique handle to identify received data 17 | * @param tables Tables to subscribe 18 | * @param symbols Symbols to subscribe 19 | * @throws QConnectorException If the communication fails or the subscription is corrupt 20 | */ 21 | void subscribe(String handle, String[] tables, String[] symbols) throws QConnectorException; 22 | 23 | /** 24 | * Subscribe and receive data via local QConnectorDataListener. 25 | * 26 | * @param listener Listener 27 | * @param tables Table to subscribe 28 | * @param symbols Symbols to subscribe 29 | * @throws QConnectorException If the communication fails or the subscription is corrupt 30 | */ 31 | void subscribe(QConnectorDataListener listener, String[] tables, String[] symbols) throws QConnectorException; 32 | 33 | /** 34 | * Unsubscribe. 35 | * 36 | * @param handle Unique handle to identify received data 37 | */ 38 | void unsubscribe(String handle); 39 | 40 | /** 41 | * Unsubscribe. 42 | * 43 | * @param listener Listener 44 | */ 45 | void unsubscribe(QConnectorDataListener listener); 46 | 47 | /** 48 | * @return QConnectorListener 49 | */ 50 | QConnectorListener getConnectorListener(); 51 | 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/info/michaelwittig/javaq/connector/QConnectorDataListener.java: -------------------------------------------------------------------------------- 1 | package info.michaelwittig.javaq.connector; 2 | 3 | import info.michaelwittig.javaq.query.Result; 4 | 5 | /** 6 | * QConnector data listener. 7 | * 8 | * @author mwittig 9 | * 10 | */ 11 | public interface QConnectorDataListener { 12 | 13 | /** 14 | * QConnectorException occurred. Must be handled by yourself! 15 | * 16 | * @param e Exception 17 | */ 18 | void exception(QConnectorException e); 19 | 20 | /** 21 | * Result received. 22 | * 23 | * @param handle Unique handle to identify received data 24 | * @param result Result 25 | */ 26 | void resultReceived(Result result); 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/info/michaelwittig/javaq/connector/QConnectorError.java: -------------------------------------------------------------------------------- 1 | package info.michaelwittig.javaq.connector; 2 | 3 | /** 4 | * QConnectorErrors are handled by the QConnector. They are just an information. Nothing needs to be done! 5 | * 6 | * @author mwittig 7 | * 8 | */ 9 | public final class QConnectorError extends Exception { 10 | 11 | /** Serial version UID. */ 12 | private static final long serialVersionUID = 1L; 13 | 14 | 15 | /** 16 | * @param message Message 17 | */ 18 | public QConnectorError(final String message) { 19 | super(message); 20 | } 21 | 22 | @Override 23 | public String toString() { 24 | return "QConnectorError: " + this.getMessage(); 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/info/michaelwittig/javaq/connector/QConnectorException.java: -------------------------------------------------------------------------------- 1 | package info.michaelwittig.javaq.connector; 2 | 3 | /** 4 | * QConnectorExceptions must be handled by yourself! They are indicating that the system is not in a proper state any longer. 5 | * 6 | * @author mwittig 7 | * 8 | */ 9 | public final class QConnectorException extends Exception { 10 | 11 | /** Serial version UID. */ 12 | private static final long serialVersionUID = 1L; 13 | 14 | 15 | /** 16 | * @param message Message 17 | * @param cause Cause 18 | */ 19 | public QConnectorException(final String message, final Throwable cause) { 20 | super(message, cause); 21 | } 22 | 23 | /** 24 | * @param message Message 25 | */ 26 | public QConnectorException(final String message) { 27 | super(message); 28 | } 29 | 30 | @Override 31 | public String toString() { 32 | return "QConnectorException: " + this.getMessage(); 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/info/michaelwittig/javaq/connector/QConnectorListener.java: -------------------------------------------------------------------------------- 1 | package info.michaelwittig.javaq.connector; 2 | 3 | import info.michaelwittig.javaq.query.Result; 4 | 5 | /** 6 | * QConnector listener. 7 | * 8 | * @author mwittig 9 | * 10 | */ 11 | public interface QConnectorListener { 12 | 13 | /** 14 | * QConnectorError occurred. Is handled by the QConnector. Just an information. 15 | * 16 | * @param e Error 17 | */ 18 | void error(QConnectorError e); 19 | 20 | /** 21 | * QConnectorException occurred. Must be handled by yourself! 22 | * 23 | * @param e Exception 24 | */ 25 | void exception(QConnectorException e); 26 | 27 | /** 28 | * Result received. 29 | * 30 | * @param handle Unique handle to identify received data 31 | * @param result Result 32 | */ 33 | void resultReceived(final String handle, Result result); 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/info/michaelwittig/javaq/connector/QConnectorSync.java: -------------------------------------------------------------------------------- 1 | package info.michaelwittig.javaq.connector; 2 | 3 | import info.michaelwittig.javaq.query.Function; 4 | import info.michaelwittig.javaq.query.Result; 5 | import info.michaelwittig.javaq.query.Select; 6 | 7 | /** 8 | * QConnector synchronous. 9 | * 10 | * Thread-safe? depends on implementation 11 | * 12 | * @author mwittig 13 | * 14 | */ 15 | public interface QConnectorSync extends QConnector { 16 | 17 | /** 18 | * Synchronous execute. 19 | * 20 | * @param q Q code 21 | * @return Result 22 | * @throws QConnectorException If something went wrong 23 | */ 24 | Result execute(String q) throws QConnectorException; 25 | 26 | /** 27 | * Synchronous select. 28 | * 29 | * @param select Select 30 | * @return Result 31 | * @throws QConnectorException If something went wrong 32 | */ 33 | Result select(Select select) throws QConnectorException; 34 | 35 | /** 36 | * Synchronous call. 37 | * 38 | * @param function Function 39 | * @return Result 40 | * @throws QConnectorException If something went wrong 41 | */ 42 | Result call(Function function) throws QConnectorException; 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/info/michaelwittig/javaq/connector/impl/CResultHelper.java: -------------------------------------------------------------------------------- 1 | package info.michaelwittig.javaq.connector.impl; 2 | 3 | import info.michaelwittig.javaq.connector.QConnectorException; 4 | import info.michaelwittig.javaq.query.EmptyResult; 5 | import info.michaelwittig.javaq.query.FlipFlipResult; 6 | import info.michaelwittig.javaq.query.FlipResult; 7 | import info.michaelwittig.javaq.query.ListResult; 8 | import info.michaelwittig.javaq.query.PrimitiveResult; 9 | import info.michaelwittig.javaq.query.Result; 10 | 11 | import java.util.UUID; 12 | 13 | import kx.c; 14 | 15 | import org.apache.commons.lang3.ArrayUtils; 16 | 17 | /** 18 | * @author mwittig 19 | * 20 | */ 21 | public final class CResultHelper { 22 | 23 | /** 24 | * Converts an result from the kx c library to a QConnector Result. 25 | * 26 | * @param res Result from q 27 | * @return Result 28 | * @throws QConnectorException If the result type is not supported 29 | */ 30 | public static Result convert(final Object res) throws QConnectorException { 31 | if (res == null) { 32 | return new EmptyResult(); 33 | } 34 | // table 35 | if (res instanceof c.Flip) { 36 | return new FlipResult("", (c.Flip) res); 37 | } 38 | // dict 39 | if (res instanceof c.Dict) { 40 | final c.Dict dict = (c.Dict) res; 41 | if ((dict.x instanceof c.Flip) && (dict.y instanceof c.Flip)) { 42 | final c.Flip key = (c.Flip) dict.x; 43 | final c.Flip data = (c.Flip) dict.y; 44 | return new FlipFlipResult("", key, data); 45 | } 46 | } 47 | if (res instanceof Object[]) { 48 | final Object[] oa = (Object[]) res; 49 | if ((oa.length == 2) && (oa[0] instanceof String) && (oa[1] instanceof c.Flip)) { 50 | final String table = (String) oa[0]; 51 | final c.Flip flip = (c.Flip) oa[1]; 52 | return new FlipResult(table, flip); 53 | } else if ((oa.length == 3) && (oa[1] instanceof String) && (oa[2] instanceof c.Flip)) { 54 | final String table = (String) oa[1]; 55 | final c.Flip flip = (c.Flip) oa[2]; 56 | return new FlipResult(table, flip); 57 | } else { 58 | return new ListResult(oa); 59 | } 60 | } 61 | // list 62 | if (res instanceof byte[]) { 63 | return new ListResult(ArrayUtils.toObject((byte[]) res)); 64 | } 65 | if (res instanceof boolean[]) { 66 | return new ListResult(ArrayUtils.toObject((boolean[]) res)); 67 | } 68 | if (res instanceof short[]) { 69 | return new ListResult(ArrayUtils.toObject((short[]) res)); 70 | } 71 | if (res instanceof int[]) { 72 | return new ListResult(ArrayUtils.toObject((int[]) res)); 73 | } 74 | if (res instanceof long[]) { 75 | return new ListResult(ArrayUtils.toObject((long[]) res)); 76 | } 77 | if (res instanceof float[]) { 78 | return new ListResult(ArrayUtils.toObject((float[]) res)); // q real 79 | } 80 | if (res instanceof double[]) { 81 | return new ListResult(ArrayUtils.toObject((double[]) res)); // q float 82 | } 83 | if (res instanceof char[]) { 84 | return new ListResult(ArrayUtils.toObject((char[]) res)); 85 | } 86 | if (res.getClass().isArray()) { 87 | if (res.getClass().getComponentType() == String.class) { // q symbol 88 | return new ListResult((String[]) res); 89 | } 90 | if (res.getClass().getComponentType() == java.sql.Timestamp.class) { // q timestamp 91 | return new ListResult((java.sql.Timestamp[]) res); 92 | } 93 | if (res.getClass().getComponentType() == kx.c.Minute.class) { // q minute 94 | return new ListResult((kx.c.Minute[]) res); 95 | } 96 | if (res.getClass().getComponentType() == kx.c.Second.class) { // q second 97 | return new ListResult((kx.c.Second[]) res); 98 | } 99 | if (res.getClass().getComponentType() == kx.c.Month.class) { // q month 100 | return new ListResult((kx.c.Month[]) res); 101 | } 102 | if (res.getClass().getComponentType() == java.sql.Time.class) { // q time 103 | return new ListResult((java.sql.Time[]) res); 104 | } 105 | if (res.getClass().getComponentType() == java.sql.Date.class) { // q date 106 | return new ListResult((java.sql.Date[]) res); 107 | } 108 | if (res.getClass().getComponentType() == java.util.Date.class) { // q datetime 109 | return new ListResult((java.util.Date[]) res); 110 | } 111 | if (res.getClass().getComponentType() == kx.c.Timespan.class) { // q timespan 112 | return new ListResult((kx.c.Timespan[]) res); 113 | } 114 | if (res.getClass().getComponentType() == UUID.class) { // q guid 115 | return new ListResult((UUID[]) res); 116 | } 117 | } 118 | // primitive 119 | if (res instanceof Boolean) { 120 | return new PrimitiveResult((Boolean) res); 121 | } 122 | if (res instanceof Byte) { 123 | return new PrimitiveResult((Byte) res); 124 | } 125 | if (res instanceof Short) { 126 | return new PrimitiveResult((Short) res); 127 | } 128 | if (res instanceof Long) { 129 | return new PrimitiveResult((Long) res); 130 | } 131 | if (res instanceof Integer) { 132 | return new PrimitiveResult((Integer) res); 133 | } 134 | if (res instanceof Float) { // q real 135 | return new PrimitiveResult((Float) res); 136 | } 137 | if (res instanceof Double) { // q float 138 | return new PrimitiveResult((Double) res); 139 | } 140 | if (res instanceof Character) { // q char 141 | return new PrimitiveResult((Character) res); 142 | } 143 | if (res instanceof String) { // q symbol 144 | return new PrimitiveResult((String) res); 145 | } 146 | if (res instanceof java.sql.Timestamp) { // q timestamp 147 | return new PrimitiveResult((java.sql.Timestamp) res); 148 | } 149 | if (res instanceof kx.c.Minute) { // q minute 150 | return new PrimitiveResult((kx.c.Minute) res); 151 | } 152 | if (res instanceof kx.c.Second) { // q second 153 | return new PrimitiveResult((kx.c.Second) res); 154 | } 155 | if (res instanceof kx.c.Month) { // q month 156 | return new PrimitiveResult((kx.c.Month) res); 157 | } 158 | if (res instanceof java.sql.Time) { // q time 159 | return new PrimitiveResult((java.sql.Time) res); 160 | } 161 | if (res instanceof java.sql.Date) { // q date 162 | return new PrimitiveResult((java.sql.Date) res); 163 | } 164 | if (res instanceof java.util.Date) { // q datetime 165 | return new PrimitiveResult((java.util.Date) res); 166 | } 167 | if (res instanceof kx.c.Timespan) { // q timespan 168 | return new PrimitiveResult((kx.c.Timespan) res); 169 | } 170 | if (res instanceof UUID) { // q guid 171 | return new PrimitiveResult((UUID) res); 172 | } 173 | throw new QConnectorException("Unsupported sync result type: " + res.getClass()); 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /src/main/java/info/michaelwittig/javaq/connector/impl/QConnectorFactory.java: -------------------------------------------------------------------------------- 1 | package info.michaelwittig.javaq.connector.impl; 2 | 3 | import info.michaelwittig.javaq.connector.QConnectorAsync; 4 | import info.michaelwittig.javaq.connector.QConnectorError; 5 | import info.michaelwittig.javaq.connector.QConnectorListener; 6 | import info.michaelwittig.javaq.connector.QConnectorSync; 7 | import info.michaelwittig.javaq.connector.QConnectorException; 8 | 9 | /** 10 | * Creates QConnectors. 11 | * 12 | * @author mwittig 13 | * 14 | */ 15 | public final class QConnectorFactory { 16 | 17 | /** 18 | * @param listener Listener 19 | * @param host Host 20 | * @param port Port 21 | * @param reconnectOnError Reconnect on error? 22 | * @param connect Connect? 23 | * @throws QConnectorException If the connection can not be established 24 | * @throws QConnectorError If the QConnector is already connected 25 | * @return QConnector 26 | */ 27 | public static QConnectorAsync create(final QConnectorListener listener, final String host, final int port, final boolean reconnectOnError, final boolean connect) throws QConnectorException, QConnectorError { 28 | final QConnectorAsync c = QConnectorFactory.create(listener, host, port, reconnectOnError); 29 | if (connect) { 30 | c.connect(); 31 | } 32 | return c; 33 | } 34 | 35 | /** 36 | * @param listener Listener 37 | * @param host Host 38 | * @param port Port 39 | * @param reconnectOnError Reconnect on error? 40 | * @return QConnector 41 | */ 42 | public static QConnectorAsync create(final QConnectorListener listener, final String host, final int port, final boolean reconnectOnError) { 43 | final QConnectorAsync c = new QConnectorAsyncImpl(listener, host, port, reconnectOnError); 44 | return c; 45 | } 46 | 47 | /** 48 | * @param host Host 49 | * @param port Port 50 | * @param reconnectOnError Reconnect on error? 51 | * @param threadsafe Thread-safe implementation? 52 | * @param connect Connect? 53 | * @throws QConnectorException If the connection can not be established 54 | * @throws QConnectorError If the QConnector is already connected 55 | * @return QConnector 56 | */ 57 | public static QConnectorSync create(final String host, final int port, final boolean reconnectOnError, final boolean threadsafe, final boolean connect) throws QConnectorException, QConnectorError { 58 | final QConnectorSync c = QConnectorFactory.create(host, port, reconnectOnError, threadsafe); 59 | if (connect) { 60 | c.connect(); 61 | } 62 | return c; 63 | } 64 | 65 | /** 66 | * @param host Host 67 | * @param port Port 68 | * @param reconnectOnError Reconnect on error? 69 | * @param threadsafe Thread-safe implementation? 70 | * @return QConnector 71 | */ 72 | public static QConnectorSync create(final String host, final int port, final boolean reconnectOnError, final boolean threadsafe) { 73 | final QConnectorSync c; 74 | if (threadsafe) { 75 | c = new QConnectorSyncTSImpl(host, port, reconnectOnError); 76 | } else { 77 | c = new QConnectorSyncImpl(host, port, reconnectOnError); 78 | } 79 | return c; 80 | } 81 | 82 | /** 83 | * Reconnect on error and is thread-safe. 84 | * 85 | * @param host Host 86 | * @param port Port 87 | * @return QConnector 88 | */ 89 | public static QConnectorSync create(final String host, final int port) { 90 | return create(host, port, true, true); 91 | } 92 | 93 | /** */ 94 | private QConnectorFactory() { 95 | super(); 96 | } 97 | 98 | } 99 | -------------------------------------------------------------------------------- /src/main/java/info/michaelwittig/javaq/connector/impl/QConnectorImpl.java: -------------------------------------------------------------------------------- 1 | package info.michaelwittig.javaq.connector.impl; 2 | 3 | import info.michaelwittig.javaq.connector.QConnector; 4 | 5 | /** 6 | * Q Connector implementation. 7 | * 8 | * @author mwittig 9 | * 10 | */ 11 | abstract class QConnectorImpl implements QConnector { 12 | 13 | /** Reconnect timeout offset per try. */ 14 | protected static final int RECONNECT_OFFSET_PER_TRY = 1000; 15 | 16 | /** Host. */ 17 | private final String host; 18 | 19 | /** Port. */ 20 | private final int port; 21 | 22 | /** Reconnect on error? */ 23 | private final boolean reconnectOnError; 24 | 25 | 26 | /** 27 | * @param aHost Host 28 | * @param aPort Port 29 | * @param aReconnectOnError Reconnect on error? 30 | */ 31 | protected QConnectorImpl(final String aHost, final int aPort, final boolean aReconnectOnError) { 32 | super(); 33 | this.host = aHost; 34 | this.port = aPort; 35 | this.reconnectOnError = aReconnectOnError; 36 | } 37 | 38 | @Override 39 | public final String getHost() { 40 | return this.host; 41 | } 42 | 43 | @Override 44 | public final int getPort() { 45 | return this.port; 46 | } 47 | 48 | @Override 49 | public final boolean reconnectOnError() { 50 | return this.reconnectOnError; 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/info/michaelwittig/javaq/connector/impl/QConnectorSyncImpl.java: -------------------------------------------------------------------------------- 1 | package info.michaelwittig.javaq.connector.impl; 2 | 3 | import info.michaelwittig.javaq.connector.QConnectorError; 4 | import info.michaelwittig.javaq.connector.QConnectorSync; 5 | import info.michaelwittig.javaq.connector.QConnectorException; 6 | import info.michaelwittig.javaq.connector.impl.cmd.ConnectorSyncCommand; 7 | import info.michaelwittig.javaq.connector.impl.cmd.ConnectorSyncCommandFunction; 8 | import info.michaelwittig.javaq.connector.impl.cmd.ConnectorSyncCommandQ; 9 | import info.michaelwittig.javaq.connector.impl.cmd.ConnectorSyncCommandSelect; 10 | import info.michaelwittig.javaq.query.Function; 11 | import info.michaelwittig.javaq.query.Result; 12 | import info.michaelwittig.javaq.query.Select; 13 | 14 | import java.io.IOException; 15 | import java.util.TimeZone; 16 | 17 | import kx.c; 18 | import kx.c.KException; 19 | 20 | /** 21 | * Q Connector implementation. 22 | * 23 | * Thread-safe? no 24 | * 25 | * @author mwittig 26 | * 27 | */ 28 | final class QConnectorSyncImpl extends QConnectorImpl implements QConnectorSync { 29 | 30 | /** Connection. */ 31 | private c c = null; 32 | 33 | 34 | /** 35 | * @param aHost Host 36 | * @param aPort Port 37 | * @param aReconnectOnError Reconnect on error? 38 | */ 39 | protected QConnectorSyncImpl(final String aHost, final int aPort, final boolean aReconnectOnError) { 40 | super(aHost, aPort, aReconnectOnError); 41 | } 42 | 43 | @Override 44 | public void connect() throws QConnectorException, QConnectorError { 45 | try { 46 | if (this.c != null) { 47 | throw new QConnectorError("Already connected"); 48 | } 49 | this.c = new c(this.getHost(), this.getPort()); 50 | this.c.tz = TimeZone.getTimeZone("UTC"); 51 | } catch (final KException e) { 52 | throw new QConnectorException("KException", e); 53 | } catch (final IOException e) { 54 | throw new QConnectorException("Could not connect to " + this.getHost() + ":" + this.getPort(), e); 55 | } 56 | } 57 | 58 | @Override 59 | public void disconnect() throws QConnectorError { 60 | if (this.c == null) { 61 | throw new QConnectorError("Not connected"); 62 | } 63 | try { 64 | this.c.close(); 65 | } catch (final IOException e) { 66 | // supress 67 | } 68 | this.c = null; 69 | } 70 | 71 | private Result cmd(final ConnectorSyncCommand cmd) throws QConnectorException { 72 | return this.cmd(cmd, false); 73 | } 74 | 75 | private Result cmd(final ConnectorSyncCommand cmd, final boolean isReconnectAttemp) throws QConnectorException { 76 | if (!this.isConnected()) { 77 | try { 78 | this.connect(); 79 | } catch (final QConnectorError e) { 80 | // can not happen 81 | } 82 | } 83 | try { 84 | return cmd.execute(this.c); 85 | } catch (final IOException e) { 86 | if ((isReconnectAttemp == false) && this.reconnectOnError()) { 87 | try { 88 | Thread.sleep(QConnectorImpl.RECONNECT_OFFSET_PER_TRY); 89 | } catch (final InterruptedException ie) { 90 | // supress 91 | } 92 | try { 93 | this.disconnect(); 94 | this.connect(); 95 | } catch (final QConnectorError kxe) { 96 | throw new QConnectorException("Can not reconnect", kxe); 97 | } 98 | return this.cmd(cmd, true); 99 | } 100 | throw new QConnectorException("Socket failed", e); 101 | } catch (final KException e) { 102 | throw new QConnectorException("Q failed", e); 103 | } 104 | } 105 | 106 | @Override 107 | public Result execute(final String q) throws QConnectorException { 108 | return this.cmd(new ConnectorSyncCommandQ(q)); 109 | } 110 | 111 | @Override 112 | public Result select(final Select select) throws QConnectorException { 113 | return this.cmd(new ConnectorSyncCommandSelect(select)); 114 | } 115 | 116 | @Override 117 | public Result call(final Function function) throws QConnectorException { 118 | return this.cmd(new ConnectorSyncCommandFunction(function)); 119 | } 120 | 121 | @Override 122 | public boolean isConnected() { 123 | if (this.c != null) { 124 | return true; 125 | } 126 | return false; 127 | } 128 | 129 | } 130 | -------------------------------------------------------------------------------- /src/main/java/info/michaelwittig/javaq/connector/impl/QConnectorSyncTSImpl.java: -------------------------------------------------------------------------------- 1 | package info.michaelwittig.javaq.connector.impl; 2 | 3 | import info.michaelwittig.javaq.connector.QConnectorError; 4 | import info.michaelwittig.javaq.connector.QConnectorSync; 5 | import info.michaelwittig.javaq.connector.QConnectorException; 6 | import info.michaelwittig.javaq.connector.impl.cmd.ConnectorSyncCommand; 7 | import info.michaelwittig.javaq.connector.impl.cmd.ConnectorSyncCommandFunction; 8 | import info.michaelwittig.javaq.connector.impl.cmd.ConnectorSyncCommandQ; 9 | import info.michaelwittig.javaq.connector.impl.cmd.ConnectorSyncCommandSelect; 10 | import info.michaelwittig.javaq.query.Function; 11 | import info.michaelwittig.javaq.query.Result; 12 | import info.michaelwittig.javaq.query.Select; 13 | 14 | import java.io.IOException; 15 | import java.util.TimeZone; 16 | import java.util.concurrent.BlockingQueue; 17 | import java.util.concurrent.ExecutionException; 18 | import java.util.concurrent.LinkedBlockingQueue; 19 | import java.util.concurrent.atomic.AtomicBoolean; 20 | import java.util.concurrent.atomic.AtomicReference; 21 | 22 | import kx.c; 23 | import kx.c.KException; 24 | 25 | import com.google.common.util.concurrent.SettableFuture; 26 | 27 | /** 28 | * Q Connector implementation. 29 | * 30 | * Thread-safe? yes 31 | * 32 | * @author mwittig 33 | * 34 | */ 35 | final class QConnectorSyncTSImpl extends QConnectorImpl implements QConnectorSync { 36 | 37 | /** Stop command. */ 38 | private final static ConnectorSyncCommandQ STOP_COMMAND = new ConnectorSyncCommandQ(""); 39 | 40 | /** Start command. */ 41 | private final static ConnectorSyncCommandQ START_COMMAND = new ConnectorSyncCommandQ(""); 42 | 43 | /** Empty response. */ 44 | private final static Result EMPTY_RESULT = new Result() { 45 | // nothing to do here 46 | }; 47 | 48 | /** Commands. */ 49 | private final BlockingQueue commands = new LinkedBlockingQueue(); 50 | 51 | /** Executor. */ 52 | private final AtomicReference executor = new AtomicReference(null); 53 | 54 | 55 | /** 56 | * @param aHost Host 57 | * @param aPort Port 58 | * @param aReconnectOnError Reconnect on error? 59 | */ 60 | protected QConnectorSyncTSImpl(String aHost, int aPort, boolean aReconnectOnError) { 61 | super(aHost, aPort, aReconnectOnError); 62 | } 63 | 64 | @Override 65 | public void connect() throws QConnectorException, QConnectorError { 66 | if (!this.executor.compareAndSet(null, new Executor())) { 67 | throw new QConnectorError("Already connected"); 68 | } 69 | final SettableFuture future = SettableFuture.create(); 70 | this.commands.offer(new ConnectorSyncCommandWithFutureValue(QConnectorSyncTSImpl.START_COMMAND, future)); 71 | new Thread(this.executor.get()).start(); 72 | try { 73 | future.get(); 74 | } catch (final ExecutionException e) { 75 | throw new QConnectorException("KException", e); 76 | } catch (final InterruptedException e) { 77 | throw new QConnectorException("KException", e); 78 | } 79 | } 80 | 81 | @Override 82 | public void disconnect() throws QConnectorError { 83 | final Executor old = this.executor.get(); 84 | if (old == null) { 85 | throw new QConnectorError("Not connected"); 86 | } 87 | if (!this.executor.compareAndSet(old, null)) { 88 | throw new QConnectorError("Already disconnected"); 89 | } 90 | final SettableFuture future = SettableFuture.create(); 91 | this.commands.offer(new ConnectorSyncCommandWithFutureValue(QConnectorSyncTSImpl.STOP_COMMAND, future)); 92 | } 93 | 94 | @Override 95 | public boolean isConnected() { 96 | if (this.executor.get() != null) { 97 | return true; 98 | } 99 | return false; 100 | } 101 | 102 | private Result cmd(final ConnectorSyncCommand cmd) throws QConnectorException { 103 | final SettableFuture future = SettableFuture.create(); 104 | this.commands.offer(new ConnectorSyncCommandWithFutureValue(cmd, future)); 105 | try { 106 | return future.get(); 107 | } catch (final InterruptedException e) { 108 | throw new QConnectorException(e.getMessage()); 109 | } catch (final ExecutionException e) { 110 | throw new QConnectorException(e.getMessage()); 111 | } 112 | } 113 | 114 | @Override 115 | public Result execute(final String q) throws QConnectorException { 116 | return this.cmd(new ConnectorSyncCommandQ(q)); 117 | } 118 | 119 | @Override 120 | public Result select(final Select select) throws QConnectorException { 121 | return this.cmd(new ConnectorSyncCommandSelect(select)); 122 | } 123 | 124 | @Override 125 | public Result call(final Function function) throws QConnectorException { 126 | return this.cmd(new ConnectorSyncCommandFunction(function)); 127 | } 128 | 129 | 130 | /** 131 | * Executor. 132 | * 133 | * @author mwittig 134 | * 135 | */ 136 | private final class Executor implements Runnable { 137 | 138 | @Override 139 | public void run() { 140 | c c = null; 141 | while (true) { 142 | final ConnectorSyncCommandWithFutureValue t; 143 | try { 144 | t = QConnectorSyncTSImpl.this.commands.take(); 145 | } catch (final InterruptedException e) { 146 | continue; 147 | } 148 | 149 | final ConnectorSyncCommand cmd = t.getCmd(); 150 | final SettableFuture future = t.getFuture(); 151 | 152 | if (cmd == QConnectorSyncTSImpl.START_COMMAND) { 153 | if (c != null) { 154 | future.setException(new QConnectorError("Already connected")); 155 | continue; 156 | } 157 | 158 | try { 159 | c = new c(QConnectorSyncTSImpl.this.getHost(), QConnectorSyncTSImpl.this.getPort()); 160 | c.tz = TimeZone.getTimeZone("UTC"); 161 | future.set(QConnectorSyncTSImpl.EMPTY_RESULT); 162 | } catch (final KException e) { 163 | future.setException(e); 164 | } catch (final IOException e) { 165 | future.setException(e); 166 | } 167 | } else { 168 | if (c == null) { 169 | try { 170 | try { 171 | Thread.sleep(QConnectorImpl.RECONNECT_OFFSET_PER_TRY); 172 | } catch (final InterruptedException ie) { 173 | // suppress 174 | } 175 | c = new c(QConnectorSyncTSImpl.this.getHost(), QConnectorSyncTSImpl.this.getPort()); 176 | } catch (final KException e) { 177 | future.setException(e); 178 | continue; 179 | } catch (final IOException e) { 180 | future.setException(e); 181 | continue; 182 | } 183 | } 184 | 185 | if (cmd == QConnectorSyncTSImpl.STOP_COMMAND) { 186 | try { 187 | c.close(); 188 | c = null; 189 | } catch (final Exception e) { 190 | // suppress 191 | } 192 | future.set(QConnectorSyncTSImpl.EMPTY_RESULT); 193 | break; 194 | } 195 | 196 | try { 197 | final Result result = cmd.execute(c); 198 | future.set(result); 199 | } catch (final QConnectorException e) { 200 | future.setException(e); 201 | } catch (final KException e) { 202 | future.setException(new QConnectorException("Q failed", e)); 203 | } catch (final IOException e) { 204 | if ((t.tryReconnect() == true) && QConnectorSyncTSImpl.this.reconnectOnError()) { 205 | c = null; 206 | QConnectorSyncTSImpl.this.commands.offer(t); 207 | } else { 208 | future.setException(new QConnectorException("Could not talk to " + QConnectorSyncTSImpl.this.getHost() + ":" + QConnectorSyncTSImpl.this.getPort(), e)); 209 | } 210 | } 211 | } 212 | } 213 | } 214 | } 215 | 216 | /** 217 | * @author mwittig 218 | * 219 | * Wraps a command with a FutureValue. 220 | */ 221 | private final static class ConnectorSyncCommandWithFutureValue { 222 | 223 | /** Command. */ 224 | private final ConnectorSyncCommand cmd; 225 | 226 | /** Result future. */ 227 | private final SettableFuture future; 228 | 229 | /** Indicates if the command is in the queue the 3nd time (reconnect). */ 230 | private final AtomicBoolean reconnectTry = new AtomicBoolean(false); 231 | 232 | 233 | /** 234 | * @param cmd Command 235 | * @param future Result future 236 | */ 237 | public ConnectorSyncCommandWithFutureValue(final ConnectorSyncCommand cmd, final SettableFuture future) { 238 | super(); 239 | this.cmd = cmd; 240 | this.future = future; 241 | } 242 | 243 | /** 244 | * @return Command 245 | */ 246 | public ConnectorSyncCommand getCmd() { 247 | return this.cmd; 248 | } 249 | 250 | /** 251 | * @return Result future 252 | */ 253 | public SettableFuture getFuture() { 254 | return this.future; 255 | } 256 | 257 | /** 258 | * @return true if the command was not yet in reconnect state 259 | */ 260 | public boolean tryReconnect() { 261 | return this.reconnectTry.compareAndSet(false, true); 262 | } 263 | 264 | } 265 | } 266 | -------------------------------------------------------------------------------- /src/main/java/info/michaelwittig/javaq/connector/impl/cmd/AConnectorSyncCommand.java: -------------------------------------------------------------------------------- 1 | package info.michaelwittig.javaq.connector.impl.cmd; 2 | 3 | import info.michaelwittig.javaq.connector.QConnectorException; 4 | import info.michaelwittig.javaq.connector.impl.CResultHelper; 5 | import info.michaelwittig.javaq.query.Result; 6 | 7 | import java.io.IOException; 8 | 9 | import kx.c; 10 | import kx.c.KException; 11 | 12 | /** 13 | * Abstract connector synchronous command. 14 | * 15 | * @author mwittig 16 | * 17 | */ 18 | public abstract class AConnectorSyncCommand implements ConnectorSyncCommand { 19 | 20 | /** 21 | * @param c C 22 | * @param q Q 23 | * @return Result 24 | * @throws QConnectorException If something went wrong 25 | * @throws KException If something went wrong 26 | * @throws IOException If something went wrong 27 | */ 28 | protected final Result execute(final c c, final String q) throws QConnectorException, KException, IOException { 29 | final Object res = c.k(q); 30 | return CResultHelper.convert(res); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/info/michaelwittig/javaq/connector/impl/cmd/ConnectorAsyncCommand.java: -------------------------------------------------------------------------------- 1 | package info.michaelwittig.javaq.connector.impl.cmd; 2 | 3 | import info.michaelwittig.javaq.connector.QConnectorException; 4 | 5 | import java.io.IOException; 6 | 7 | import kx.c; 8 | import kx.c.KException; 9 | 10 | /** 11 | * Connector command without result. 12 | * 13 | * @author mwittig 14 | * 15 | */ 16 | public interface ConnectorAsyncCommand { 17 | 18 | /** 19 | * Execute the command via c. 20 | * 21 | * @param c C 22 | * @throws QConnectorException If something went wrong 23 | * @throws KException If something went wrong in q 24 | * @throws IOException If something went wrong on the transport layer 25 | */ 26 | void execute(final c c) throws QConnectorException, KException, IOException; 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/info/michaelwittig/javaq/connector/impl/cmd/ConnectorAsyncCommandQ.java: -------------------------------------------------------------------------------- 1 | package info.michaelwittig.javaq.connector.impl.cmd; 2 | 3 | import java.io.IOException; 4 | 5 | import kx.c; 6 | import kx.c.KException; 7 | 8 | /** 9 | * Connector Q command with result. 10 | * 11 | * @author mwittig 12 | * 13 | */ 14 | public final class ConnectorAsyncCommandQ implements ConnectorAsyncCommand { 15 | 16 | /** Q. */ 17 | private final String q; 18 | 19 | 20 | /** 21 | * @param aQ Q 22 | */ 23 | public ConnectorAsyncCommandQ(final String aQ) { 24 | super(); 25 | this.q = aQ; 26 | } 27 | 28 | @Override 29 | public void execute(final c c) throws KException, IOException { 30 | c.ks(this.q); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/info/michaelwittig/javaq/connector/impl/cmd/ConnectorSyncCommand.java: -------------------------------------------------------------------------------- 1 | package info.michaelwittig.javaq.connector.impl.cmd; 2 | 3 | import info.michaelwittig.javaq.connector.QConnectorException; 4 | import info.michaelwittig.javaq.query.Result; 5 | 6 | import java.io.IOException; 7 | 8 | import kx.c; 9 | import kx.c.KException; 10 | 11 | /** 12 | * Connector command with result. 13 | * 14 | * @author mwittig 15 | * 16 | */ 17 | public interface ConnectorSyncCommand { 18 | 19 | /** 20 | * Execute the command via c. 21 | * 22 | * @param c C 23 | * @return Result 24 | * @throws QConnectorException If something went wrong 25 | * @throws KException If something went wrong in q 26 | * @throws IOException If something went wrong on the transport layer 27 | */ 28 | Result execute(final c c) throws QConnectorException, KException, IOException; 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/info/michaelwittig/javaq/connector/impl/cmd/ConnectorSyncCommandFunction.java: -------------------------------------------------------------------------------- 1 | package info.michaelwittig.javaq.connector.impl.cmd; 2 | 3 | import info.michaelwittig.javaq.connector.QConnectorException; 4 | import info.michaelwittig.javaq.query.Function; 5 | import info.michaelwittig.javaq.query.Result; 6 | 7 | import java.io.IOException; 8 | 9 | import kx.c; 10 | import kx.c.KException; 11 | 12 | /** 13 | * Connector function command with result. 14 | * 15 | * @author mwittig 16 | * 17 | */ 18 | public final class ConnectorSyncCommandFunction extends AConnectorSyncCommand { 19 | 20 | /** Function. */ 21 | private final Function function; 22 | 23 | 24 | /** 25 | * @param aFunction Function 26 | */ 27 | public ConnectorSyncCommandFunction(final Function aFunction) { 28 | super(); 29 | this.function = aFunction; 30 | } 31 | 32 | @Override 33 | public Result execute(final c c) throws QConnectorException, KException, IOException { 34 | return this.execute(c, this.function.toQ()); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/info/michaelwittig/javaq/connector/impl/cmd/ConnectorSyncCommandQ.java: -------------------------------------------------------------------------------- 1 | package info.michaelwittig.javaq.connector.impl.cmd; 2 | 3 | import info.michaelwittig.javaq.connector.QConnectorException; 4 | import info.michaelwittig.javaq.query.Result; 5 | 6 | import java.io.IOException; 7 | 8 | import kx.c; 9 | import kx.c.KException; 10 | 11 | /** 12 | * Connector Q command with result. 13 | * 14 | * @author mwittig 15 | * 16 | */ 17 | public final class ConnectorSyncCommandQ extends AConnectorSyncCommand { 18 | 19 | /** Q. */ 20 | private final String q; 21 | 22 | 23 | /** 24 | * @param aQ Q 25 | */ 26 | public ConnectorSyncCommandQ(final String aQ) { 27 | super(); 28 | this.q = aQ; 29 | } 30 | 31 | @Override 32 | public Result execute(final c c) throws QConnectorException, KException, IOException { 33 | return this.execute(c, this.q); 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/info/michaelwittig/javaq/connector/impl/cmd/ConnectorSyncCommandSelect.java: -------------------------------------------------------------------------------- 1 | package info.michaelwittig.javaq.connector.impl.cmd; 2 | 3 | import info.michaelwittig.javaq.connector.QConnectorException; 4 | import info.michaelwittig.javaq.query.Result; 5 | import info.michaelwittig.javaq.query.Select; 6 | 7 | import java.io.IOException; 8 | 9 | import kx.c; 10 | import kx.c.KException; 11 | 12 | /** 13 | * Connector select command with result. 14 | * 15 | * @author mwittig 16 | * 17 | */ 18 | public final class ConnectorSyncCommandSelect extends AConnectorSyncCommand { 19 | 20 | /** Select. */ 21 | private final Select select; 22 | 23 | 24 | /** 25 | * @param aSelect Select 26 | */ 27 | public ConnectorSyncCommandSelect(final Select aSelect) { 28 | super(); 29 | this.select = aSelect; 30 | } 31 | 32 | @Override 33 | public Result execute(final c c) throws QConnectorException, KException, IOException { 34 | return this.execute(c, this.select.toQ()); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/info/michaelwittig/javaq/query/ATableResult.java: -------------------------------------------------------------------------------- 1 | package info.michaelwittig.javaq.query; 2 | 3 | /** 4 | * @author thoeger 5 | * 6 | */ 7 | public abstract class ATableResult implements TableResult { 8 | 9 | private final String name; 10 | 11 | 12 | /** 13 | * @param aName Table name 14 | */ 15 | ATableResult(final String aName) { 16 | this.name = aName; 17 | } 18 | 19 | /** 20 | * @return Table name 21 | */ 22 | public String getName() { 23 | return this.name; 24 | } 25 | 26 | @Override 27 | public String toString() { 28 | final StringBuilder sb = new StringBuilder(); 29 | for (final String col : this.getColNames()) { 30 | sb.append(String.format("%20s ", col)); 31 | } 32 | sb.append(System.getProperty("line.separator")); 33 | for (int i = 0; i < this.getCols(); i++) { 34 | sb.append(String.format("%20s ", "--")); 35 | } 36 | sb.append(System.getProperty("line.separator")); 37 | for (int i = 0; i < this.getRows(); i++) { 38 | for (final String col : this.getColNames()) { 39 | final Object value = this.getAt(col, i); 40 | if (value == null) { 41 | sb.append(String.format("%20s ", "")); 42 | } else { 43 | sb.append(String.format("%20s ", value.toString())); 44 | } 45 | } 46 | sb.append(System.getProperty("line.separator")); 47 | } 48 | return sb.toString(); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/info/michaelwittig/javaq/query/EmptyResult.java: -------------------------------------------------------------------------------- 1 | package info.michaelwittig.javaq.query; 2 | 3 | /** 4 | * Empty result. 5 | * 6 | * @author mwittig 7 | * 8 | */ 9 | public final class EmptyResult implements Result { 10 | 11 | /** */ 12 | public EmptyResult() { 13 | super(); 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/info/michaelwittig/javaq/query/FlipFlipResult.java: -------------------------------------------------------------------------------- 1 | package info.michaelwittig.javaq.query; 2 | 3 | import info.michaelwittig.javaq.query.column.Column; 4 | 5 | import java.lang.reflect.Array; 6 | import java.util.Collections; 7 | import java.util.HashMap; 8 | import java.util.Map; 9 | 10 | import kx.c; 11 | import kx.c.Flip; 12 | 13 | /** 14 | * Flip flip result. (Keyed tables) 15 | * 16 | * @author mwittig 17 | * 18 | */ 19 | public final class FlipFlipResult extends ATableResult { 20 | 21 | /** Key flip. */ 22 | private final c.Flip key; 23 | 24 | /** Data flip. */ 25 | private final c.Flip data; 26 | 27 | /** Key column name 2 index. */ 28 | private final Map keyColName2Index; 29 | 30 | /** Data column name 2 index. */ 31 | private final Map dataColName2Index; 32 | 33 | /** Column names. */ 34 | private final String[] cols; 35 | 36 | 37 | /** 38 | * @param aName Name 39 | * @param aKey Key flip 40 | * @param aData Data flip 41 | */ 42 | public FlipFlipResult(final String aName, final Flip aKey, final Flip aData) { 43 | super(aName); 44 | this.key = aKey; 45 | this.data = aData; 46 | final HashMap keyTmp = new HashMap(); 47 | for (int i = 0; i < this.key.x.length; i++) { 48 | keyTmp.put(this.key.x[i], i); 49 | } 50 | this.keyColName2Index = Collections.unmodifiableMap(keyTmp); 51 | final HashMap dataTmp = new HashMap(); 52 | for (int i = 0; i < this.data.x.length; i++) { 53 | dataTmp.put(this.data.x[i], i); 54 | } 55 | this.dataColName2Index = Collections.unmodifiableMap(dataTmp); 56 | 57 | this.cols = new String[this.getCols()]; 58 | for (int i = 0; i < this.key.x.length; i++) { 59 | this.cols[i] = this.key.x[i]; 60 | } 61 | for (int i = 0; i < this.data.x.length; i++) { 62 | this.cols[i + this.key.x.length] = this.data.x[i]; 63 | } 64 | } 65 | 66 | @Override 67 | public int getRows() { 68 | return Array.getLength(this.key.y[0]); 69 | } 70 | 71 | @Override 72 | public int getCols() { 73 | return this.key.x.length + this.data.x.length; 74 | } 75 | 76 | @Override 77 | public Object getAt(final Column col, final int row) { 78 | return this.getAt(col.getName(), row); 79 | } 80 | 81 | @Override 82 | public Object getAt(final String col, final int row) { 83 | if (this.keyColName2Index.containsKey(col)) { 84 | return this.getAt(this.key, this.keyColName2Index.get(col), row); 85 | } else if (this.dataColName2Index.containsKey(col)) { 86 | return this.getAt(this.data, this.dataColName2Index.get(col), row); 87 | } 88 | throw new IllegalArgumentException("Column " + col + " not found in keyed table"); 89 | } 90 | 91 | @Override 92 | public Object getAt(final int col, final int row) { 93 | return this.getAt(this.cols[col], row); 94 | } 95 | 96 | @Override 97 | public String[] getColNames() { 98 | return this.cols; 99 | } 100 | 101 | /** 102 | * @param flip Flip 103 | * @param col Column index 104 | * @param row Row index 105 | * @return Object or null 106 | */ 107 | private Object getAt(final Flip flip, final int col, final int row) { 108 | return c.at(flip.y[col], row); 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/main/java/info/michaelwittig/javaq/query/FlipResult.java: -------------------------------------------------------------------------------- 1 | package info.michaelwittig.javaq.query; 2 | 3 | import info.michaelwittig.javaq.query.column.Column; 4 | 5 | import java.lang.reflect.Array; 6 | import java.util.Collections; 7 | import java.util.HashMap; 8 | import java.util.Map; 9 | 10 | import kx.c; 11 | import kx.c.Flip; 12 | 13 | /** 14 | * Flip result. 15 | * 16 | * @author mwittig 17 | * 18 | */ 19 | public final class FlipResult extends ATableResult { 20 | 21 | /** Flip. */ 22 | private final c.Flip flip; 23 | 24 | /** Column name 2 index. */ 25 | private final Map colName2Index; 26 | 27 | 28 | /** 29 | * @param aName Name 30 | * @param aFlip Flip 31 | */ 32 | public FlipResult(final String aName, final Flip aFlip) { 33 | super(aName); 34 | this.flip = aFlip; 35 | final HashMap tmp = new HashMap(); 36 | for (int i = 0; i < this.flip.x.length; i++) { 37 | tmp.put(this.flip.x[i], i); 38 | } 39 | this.colName2Index = Collections.unmodifiableMap(tmp); 40 | } 41 | 42 | @Override 43 | public int getRows() { 44 | return Array.getLength(this.flip.y[0]); 45 | } 46 | 47 | @Override 48 | public int getCols() { 49 | return this.flip.x.length; 50 | } 51 | 52 | /** 53 | * @param col Column 54 | * @param row Row index 55 | * @return Object or null 56 | */ 57 | @Override 58 | public Object getAt(final Column col, final int row) { 59 | return this.getAt(col.getName(), row); 60 | } 61 | 62 | @Override 63 | public Object getAt(final String col, final int row) { 64 | if (this.colName2Index.containsKey(col)) { 65 | return this.getAt(this.colName2Index.get(col), row); 66 | } 67 | throw new IllegalArgumentException("Column " + col + " not found in table"); 68 | } 69 | 70 | @Override 71 | public String[] getColNames() { 72 | return this.flip.x; 73 | } 74 | 75 | /** 76 | * @param col Column index 77 | * @param row Row index 78 | * @return Object or null 79 | */ 80 | @Override 81 | public Object getAt(final int col, final int row) { 82 | return c.at(this.flip.y[col], row); 83 | } 84 | 85 | } 86 | -------------------------------------------------------------------------------- /src/main/java/info/michaelwittig/javaq/query/Function.java: -------------------------------------------------------------------------------- 1 | package info.michaelwittig.javaq.query; 2 | 3 | import info.michaelwittig.javaq.Builder; 4 | import info.michaelwittig.javaq.Q; 5 | import info.michaelwittig.javaq.query.value.Value; 6 | 7 | import java.util.List; 8 | 9 | /** 10 | * Select. 11 | * 12 | * @author mwittig 13 | * 14 | */ 15 | public interface Function extends Q { 16 | 17 | /** 18 | * @return Name 19 | */ 20 | String getName(); 21 | 22 | /** 23 | * @return Parameters 24 | */ 25 | @SuppressWarnings("rawtypes") 26 | List getParams(); 27 | 28 | 29 | /** 30 | * Function builder. 31 | * 32 | * @author mwittig 33 | * 34 | */ 35 | public interface FunctionBuilder extends Builder { 36 | 37 | /** 38 | * @param value Value 39 | * @return FunctionBuilder 40 | */ 41 | FunctionBuilder param(Value value); 42 | 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/info/michaelwittig/javaq/query/ListResult.java: -------------------------------------------------------------------------------- 1 | package info.michaelwittig.javaq.query; 2 | 3 | /** 4 | * List result. 5 | * 6 | * @author mwittig 7 | * 8 | * @param Java type 9 | */ 10 | public final class ListResult implements Result { 11 | 12 | /** List. */ 13 | private final J[] list; 14 | 15 | 16 | /** 17 | * @param aList List 18 | */ 19 | public ListResult(final J[] aList) { 20 | this.list = aList; 21 | } 22 | 23 | /** 24 | * @return List 25 | */ 26 | public J[] getList() { 27 | return this.list; 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/info/michaelwittig/javaq/query/PrimitiveResult.java: -------------------------------------------------------------------------------- 1 | package info.michaelwittig.javaq.query; 2 | 3 | /** 4 | * Primitive result. 5 | * 6 | * @author mwittig 7 | * 8 | * @param Java type 9 | */ 10 | public final class PrimitiveResult implements Result { 11 | 12 | /** Primitive. */ 13 | private final J primitive; 14 | 15 | 16 | /** 17 | * @param aPrimitive Primitive 18 | */ 19 | public PrimitiveResult(final J aPrimitive) { 20 | this.primitive = aPrimitive; 21 | } 22 | 23 | /** 24 | * @return Primitive 25 | */ 26 | public J getPrimitive() { 27 | return this.primitive; 28 | } 29 | 30 | @Override 31 | public String toString() { 32 | return "PrimitiveResult [primitive=" + this.primitive + "]"; 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/info/michaelwittig/javaq/query/Result.java: -------------------------------------------------------------------------------- 1 | package info.michaelwittig.javaq.query; 2 | 3 | /** 4 | * Result. 5 | * 6 | * @author mwittig 7 | * 8 | */ 9 | public interface Result { 10 | 11 | // interface body intentionally left blank 12 | 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/info/michaelwittig/javaq/query/Select.java: -------------------------------------------------------------------------------- 1 | package info.michaelwittig.javaq.query; 2 | 3 | import info.michaelwittig.javaq.Builder; 4 | import info.michaelwittig.javaq.Q; 5 | import info.michaelwittig.javaq.query.Select.Sort.Direction; 6 | import info.michaelwittig.javaq.query.column.Column; 7 | import info.michaelwittig.javaq.query.filter.Filter; 8 | import info.michaelwittig.javaq.query.group.Group; 9 | 10 | import java.util.List; 11 | 12 | /** 13 | * Select. 14 | * 15 | * @author mwittig 16 | * 17 | */ 18 | public interface Select extends Q { 19 | 20 | /** 21 | * @return Columns 22 | */ 23 | List> getColumns(); 24 | 25 | /** 26 | * @return Groups 27 | */ 28 | List getGroups(); 29 | 30 | /** 31 | * @return Table 32 | */ 33 | Table getTable(); 34 | 35 | /** 36 | * @return Filters 37 | */ 38 | List getFilters(); 39 | 40 | /** 41 | * @return Number of rows you wish to return or null (if negative, than the table is reversed!) 42 | */ 43 | Integer getNumberOfRows(); 44 | 45 | /** 46 | * @return Row number you wish to start with 47 | */ 48 | Integer getRowNumber(); 49 | 50 | /** 51 | * @return SelectSort or null 52 | */ 53 | Sort getSortColmn(); 54 | 55 | 56 | /** 57 | * Select sort. 58 | * 59 | * @author mwittig 60 | * 61 | */ 62 | public interface Sort extends Q { 63 | 64 | /** 65 | * Directions. 66 | * 67 | * @author mwittig 68 | * 69 | */ 70 | public enum Direction implements Q { 71 | /** Descending. */ 72 | descending(">"), 73 | 74 | /** Ascending. */ 75 | ascending("<"); 76 | 77 | /** Q. */ 78 | private final String q; 79 | 80 | 81 | /** 82 | * @param aQ Q 83 | */ 84 | private Direction(final String aQ) { 85 | this.q = aQ; 86 | } 87 | 88 | @Override 89 | public String toQ() { 90 | return this.q; 91 | } 92 | } 93 | 94 | 95 | /** 96 | * @return Column 97 | */ 98 | Column getColumn(); 99 | 100 | /** 101 | * @return Direction 102 | */ 103 | Direction getDirection(); 104 | 105 | } 106 | 107 | /** 108 | * Select builder. 109 | * 110 | * @author mwittig 111 | * 112 | */ 113 | public interface SelectBuilder extends Builder