I am using Embarcadero RAD Studio 10. I am trying to use Indy client/server components in my application.
I want to adjust the TCP/UDP server IP address and port at runtime.
I can see the default settings at design-time:
I can add entries to the Bindings and set the DefaultPort.
But, I want to do this while the program is running. I want to set the bindings and port in my UI and push a button to make the server use what I entered.
How do I do this?
The Bindings is a collection of TIdSocketHandle objects. Adding a new entry to the collection at design-time is the same as calling the Bindings.Add() method at runtime.
TIdSocketHandle has IP and Port properties. When a TIdSocketHandle object is created, its Port is initialized with the current value of the DefaultPort.
To do what you are asking, simply call Bindings.Add() and set the new object's IP and Port properties. For example:
Delphi:
procedure TMyForm.ConnectButtonClick(Sender: TObject);
var
LIP: string;
LPort: TIdPort;
LBinding: TIdSocketHandle;
begin
LIP := ServerIPEdit.Text;
LPort := IntToStr(ServerPortEdit.Text);
IdTCPServer1.Active := False;
IdTCPServer1.Bindings.Clear;
LBinding := IdTCPServer1.Bindings.Add;
LBinding.IP := LIP;
LBinding.Port := LPort;
IdTCPServer1.Active := True;
end;
C++:
void __fastcall TMyForm::ConnectButtonClick(TObject *Sender);
{
String LIP = ServerIPEdit->Text;
TIdPort LPort = IntToStr(ServerPortEdit->Text);
IdTCPServer1->Active = false;
IdTCPServer1->Bindings->Clear();
TIdSocketHandle *LBinding = IdTCPServer1->Bindings->Add();
LBinding->IP = LIP;
LBinding->Port = LPort;
IdTCPServer1->Active = true;
}
Same thing with TIdUDPServer.
Related
The X509ChainStatusFlags enum contains a lot of possible values: https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography.x509certificates.x509chainstatusflags?view=netframework-4.8
Are there easy ways to construct a certificate and chain that produce some of these flags? I want to construct them in order to integration-test my certificate validation logic.
Each different kind of failure requires a different amount of work to test for. Some are easy, some require heroic effort.
The easiest: error code 1: X509ChainStatusFlags.NotTimeValid.
X509Certificate2 cert = ...;
X509Chain chain = new X509Chain();
chain.ChainPolicy.VerificationTime = cert.NotBefore.AddSeconds(-1);
bool valid = chain.Build();
// valid is false, and the 0 element will have NotTimeValid as one of the reasons.
Next up: X509ChainStatusFlags.NotValidSignature.
X509Certificate2 cert = ...;
byte[] certBytes = cert.RawData;
// flip all the bits in the last byte
certBytes[certBytes.Length - 1] ^= 0xFF;
X509Certificate2 badCert = new X509Certificate2(certBytes);
chain.ChainPolicy.ApplicationPolicy.Add(new Oid("0.0", null));
bool valid = chain.Build();
// valid is false. On macOS this results in PartialChain,
// on Windows and Linux it reports NotValidSignature in element 0
Next up: X509ChainStatusFlags.NotValidForUsage.
X509Certificate2 cert = ...;
X509Chain chain = new X509Chain();
chain.ChainPolicy.ApplicationPolicy.Add(new Oid("0.0", null));
bool valid = chain.Build();
// valid is false if the certificate has an EKU extension,
// since it shouldn't contain the 0.0 OID.
// and the 0 element will report NotValidForUsage.
Some of the more complicated ones require building certificate chains incorrectly, such as making an child certificate have a NotBefore/NotAfter that isn't nestled within the CA's NotBefore/NotAfter. Some of these heroic efforts are tested in https://github.com/dotnet/runtime/blob/4f9ae42d861fcb4be2fcd5d3d55d5f227d30e723/src/libraries/System.Security.Cryptography.X509Certificates/tests/DynamicChainTests.cs and/or https://github.com/dotnet/runtime/blob/4f9ae42d861fcb4be2fcd5d3d55d5f227d30e723/src/libraries/System.Security.Cryptography.X509Certificates/tests/RevocationTests/DynamicRevocationTests.cs.
I have more globals in caché db with same data structure. For each global I defined class with SQL storage map, but I need to do it generically for all globals. Is it possible to define one class with sql storage map which will be used for mapping before every SQL query execution? I need to avoid class declaration for each global which I need to be accessible via SQL. I use ODBC for execute SQL statements.
If someone can help me, i will very appreciate it
My globals looks like this:
^glob1("x","y","SL",1) = "Name"
^glob1("x","y","SL",1,"Format") = "myFormat"
^glob1("x","y","SL",1,"Typ") = "my Type"
^glob1("x","y","SL",2) = "Name2"
^glob1("x","y","SL",2,"Format") = "myFormat2"
^glob1("x","y","SL",2,"Typ") = "Type2"
^nextGlob("x","y","SL",1) = "Next Name"
^nextGlob("x","y","SL",1,"Format") = "Next myFormat"
^nextGlob("x","y","SL",1,"Typ") = "my Type"
^another("x","y","SL",13) = "Another Name"
^another("x","y","SL",13,"Format") = "Another myFormat"
^another("x","y","SL",13,"Typ") = "Another Type"
I want to have sql access to globals using one ObjectScript class.
If you needed only read data from Caché by ODBC. So, in ODBC you can use CALL statement. And you can write some SqlProc, which can be called by ODBC.
As I can see, all of your globals with the same structure. If it so, it will be easy. You can put something like this, in your class.
Query Test() As %Query(ROWSPEC = "ID:%String,Global:%String,Name:%String,Typ:%String,Format:%String") [ SqlProc ]
{
}
ClassMethod TestExecute(ByRef qHandle As %Binary) As %Status
{
#; Initial settings
#; List of Globals
set $li(qHandle,1)=$lb("glob1","nextGlob","another")
#; Current Global index
set $li(qHandle,2)=1
#; Current ID in global
set $li(qHandle,3)=""
Quit $$$OK
}
ClassMethod TestClose(ByRef qHandle As %Binary) As %Status [ PlaceAfter = TestExecute ]
{
Quit $$$OK
}
ClassMethod TestFetch(ByRef qHandle As %Binary, ByRef Row As %List, ByRef AtEnd As %Integer = 0) As %Status [ PlaceAfter = TestExecute ]
{
set globals=$lg(qHandle,1)
set globalInd=$lg(qHandle,2)
set id=$lg(qHandle,3)
set AtEnd=1
for {
set global=$lg(globals,globalInd)
quit:global=""
set globalData="^"_global
set globalData=$na(#globalData#("x","y","SL"))
set id=$o(#globalData#(id),1,name)
if id'="" {
set AtEnd=0
set typ=$get(#globalData#(id,"Typ"))
set format=$get(#globalData#(id,"Format"))
set Row=$lb(id,global,name,typ,format)
set $li(qHandle,3)=id
quit
} elseif $i(globalInd) {
set id=""
set $li(qHandle,2)=globalInd
}
}
Quit $$$OK
}
And then you can execute statement like this
CALL pkg.classname_test()
And as a result it will be something like on this picture
If all of the globals are the same then you could do this but it is likely that your globals are all different making a single storage map unlikely. Do you already have a data dictionary/meta data system that describes your existing globals? If so I would consider writing a conversion from your existing data dictionary definition to cache classes.
Trying to get working ScalikeJDBC and SQLite. Have a simple code based on provided examples:
import scalikejdbc._, SQLInterpolation._
object Test extends App {
Class.forName("org.sqlite.JDBC")
ConnectionPool.singleton("jdbc:sqlite:test.db", null, null)
implicit val session = AutoSession
println(sql"""SELECT * FROM kv WHERE key == 'seq' LIMIT 1""".map(identity).single().apply()))
}
It fails with exception:
Exception in thread "main" java.sql.SQLException: Cannot change read-only flag after establishing a connection. Use SQLiteConfig#setReadOnly and QLiteConfig.createConnection().
at org.sqlite.SQLiteConnection.setReadOnly(SQLiteConnection.java:447)
at org.apache.commons.dbcp.DelegatingConnection.setReadOnly(DelegatingConnection.java:377)
at org.apache.commons.dbcp.PoolingDataSource$PoolGuardConnectionWrapper.setReadOnly(PoolingDataSource.java:338)
at scalikejdbc.DBConnection$class.readOnlySession(DB.scala:138)
at scalikejdbc.DB.readOnlySession(DB.scala:498)
...
I've tried both scalikejdbc 1.7 and 2.0, error remains. As sqlite driver I use "org.xerial" % "sqlite-jdbc" % "3.7.+".
What can I do to fix the error?
The following will create two separate connections, one for read-only operations and the other for writes.
ConnectionPool.add("mydb", s"jdbc:sqlite:${db.getAbsolutePath}", "", "")
ConnectionPool.add(
"mydb_ro", {
val conf = new SQLiteConfig()
conf.setReadOnly(true)
val source = new SQLiteDataSource(conf)
source.setUrl(s"jdbc:sqlite:${db.getAbsolutePath}")
new DataSourceConnectionPool(source)
}
)
I found that the reason is that you're using "org.xerial" % "sqlite-jdbc" % "3.7.15-M1". This version looks still unstable.
Use "3.7.2" as same as #kawty.
Building on #Synesso's answer, I expanded slightly to be able to get config value from config files and to set connection settings:
import scalikejdbc._
import scalikejdbc.config.TypesafeConfigReader
case class SqlLiteDataSourceConnectionPool(source: DataSource,
override val settings: ConnectionPoolSettings)
extends DataSourceConnectionPool(source)
// read settings for 'default' database
val cpSettings = TypesafeConfigReader.readConnectionPoolSettings()
val JDBCSettings(url, user, password, driver) = TypesafeConfigReader.readJDBCSettings()
// use those to create two connection pools
ConnectionPool.add("db", url, user, password, cpSettings)
ConnectionPool.add(
"db_ro", {
val conf = new SQLiteConfig()
conf.setReadOnly(true)
val source = new SQLiteDataSource(conf)
source.setUrl(url)
SqlLiteDataSourceConnectionPool(source, cpSettings)
}
)
// example using 'NamedDB'
val name: Option[String] = NamedDB("db_ro") readOnly { implicit session =>
sql"select name from users where id = $id".map(rs => rs.string("name")).single.apply()
}
This worked for me with org.xerial/sqlite-jdbc 3.28.0:
String path = ...
SQLiteConfig config = new SQLiteConfig();
config.setReadOnly(true);
return DriverManager.getConnection("jdbc:sqlite:" + path, config.toProperties());
Interestingly, I wrote a different solution on the issue on the xerial repo:
PoolProperties props = new PoolProperties();
props.setDriverClassName("org.sqlite.JDBC");
props.setUrl("jdbc:sqlite:...");
Properties extraProps = new Properties();
extraProps.setProperty("open_mode", SQLiteOpenMode.READONLY.flag + "");
props.setDbProperties(extraProps);
// This line can be left in or removed; it no longer causes a problem
// as long as the open_mode code is present.
props.setDefaultReadOnly(true);
return new DataSource(props);
I don't recall why I needed the second, and was then able to simplify it back to the first one. But if the first doesn't work, you might try the second. It uses a SQLite-specific open_mode flag that then makes it safe (but unnecessary) to use the setDefaultReadOnly call.
So I am developing this add-on using the MDN's Add-on Builder and need to connect with the SQLite Database. The connection gets created fine and insertion is fine as long as I am inserting values without binding parameters(that is, through executeSimpleSQL()). As soon as I use the createStatement() method to INSERT values, it does not work. Here's what I have done so far.
let file = FileUtils.getFile("Desk", ["my_db_file_name.sqlite"]);
let mDBConn = Services.storage.openDatabase(file);
mDBConn.executeSimpleSQL("CREATE TEMP TABLE IF NOT EXISTS element (rating VARCHAR(50))");
let stmt = mDBConn.createStatement("INSERT INTO element (rating) VALUES(:value)");
stmt.params.value = 13;
//mDBConn.executeSimpleSQL("INSERT INTO element (rating) VALUES(13)");
var statement = mDBConn.createStatement("SELECT * FROM element WHERE rating = :rat");
statement.params.rat = 13;
try {
while (statement.step()) {
let value = statement.row.rating;
console.log(value);
}
}
finally {
statement.reset();
}
Note that the SELECT statement with the bound parameters works fine, it's just the INSERT statement that's problematic.
Any ideas?
You forgot to call execute().
I have a problem like this:
1. I retrieve data from MySQL using C# ASP .Net. -- done --
2. All data from no.1 will be inserted into table on AS400. -- I got an error on this step --
Error message says that ERROR [42000] [IBM][System i Access ODBC Driver][DB2 for i5/OS]SQL0104 - Token ; was not valid. Valid tokens: <END-OF-STATEMENT>.. It's true that I used semicolon to separate queries with each others, but it's not allowed. I've Googling but I can't find the solution.
My question is what the <END-OF-STATEMENT> means of that error message..?
Here is my source code.
private static void doInsertDOCADM(MySqlConnection conn)
{
// Get Temporary table
String query = "SELECT * FROM TB_T_DOC_TEMPORARY_ADM";
DataTable dt = CSTDDBUtil.ExecuteQuery(query);
OdbcConnection as400Con = null;
as400Con = CSTDDBUtil.GetAS400Connection();
as400Con.Open();
if (dt != null && dt.Rows.Count > 0)
{
int counter = 1, maxInsertLoop = 50;
using (OdbcCommand cmd = new OdbcCommand())
{
cmd.Connection = as400Con;
foreach (DataRow dr in dt.Rows)
{
cmd.CommandText += "INSERT INTO DCDLIB.WDFDOCQ VALUES " + "(?,?,?,?);";
cmd.Parameters.Add("1", OdbcType.VarChar).Value = dr["PROD_MONTH"].ToString();
cmd.Parameters.Add("2", OdbcType.VarChar).Value = dr["NEW_MAIN_DEALER_CD"].ToString();
cmd.Parameters.Add("3", OdbcType.VarChar).Value = dr["MODEL_SERIES"].ToString();
cmd.Parameters.Add("4", OdbcType.VarChar).Value = dr["MODEL_CD"].ToString();
if (counter < maxInsertLoop)
{
counter++;
}
else
{
counter = 1;
cmd.ExecuteNonQuery();
cmd.CommandText = "";
cmd.Parameters.Clear();
}
}
if (counter > 1) cmd.ExecuteNonQuery();
}
}
Notes: I used this way (Collect some queries first, and then execute those query) to improve the performance of my application.
As Clockwork-Muse pointed out, the problem is that you can only run a single SQL statement in a command. The iSeries server does not handle multiple statements at once.
If your iSeries server is running V6R1 or later, you can use block inserts to insert multiple rows. I'm not sure if you can do so through the ODBC driver, but since you have Client Access, you should be able to install the iSeries ADO.NET driver. There are not many differences between the ADO.NET iSeries driver and the ODBC one, but with ADO.NET you get access to iSeries specific functions.
With the ADO.NET driver, multiple insert become a simple matter of :
using (iDB2Connection connection = new iDB2Connection(".... connection string ..."))
{
// Create a new SQL command
iDB2Command command =
new iDB2Command("INSERT INTO MYLIB.MYTABLE VALUES(#COL_1, #COL_2", connection);
// Initialize the parameters collection
command.DeriveParameters();
// Insert 10 rows of data at once
for (int i = 0; i < 20; i++)
{
// Here, you set your parameters for a single row
command.Parameters["#COL_1"].Value = i;
command.Parameters["#COL_2"].Value = i + 1;
// AddBatch() tells the command you're done preparing a row
command.AddBatch();
}
// The query gets executed
command.ExecuteNonQuery();
}
}
There is also some reference code provided by IBM to do block inserts using VB6 and ODBC, but I'm not sure it can be easily ported to .NET : http://publib.boulder.ibm.com/infocenter/iseries/v5r4/index.jsp?topic=%2Frzaik%2Frzaikextfetch.htm
Hope that helps.
When it says <END-OF-STATEMENT> it means about what it says - it wants that to be the end of the executed statement. I don't recall if the AS/400 allows multiple statements per execution unit (at all), but clearly it's not working here. And the driver isn't dealing with it either.
Actually, you have a larger, more fundamental problem; specifically, you're INSERTing a row at a time (usually known as row-by-agonizing-row). DB2 allows a comma-separated list of rows in a VALUES clause (so, INSERT INTO <table_name> VALUES(<row_1_columns>), (<row_2_columns>)) - does the driver you're using allow you to provide arrays (either of the entire row, or per-column)? Otherwise, look into using extract/load utilities for stuff like this - I can guarantee you that this will speed up the process.