tag:blogger.com,1999:blog-31101175301271877202024-03-29T04:29:49.947+01:00Andrea Ligios's BlogTips and Tricks involving Software Programming,
Operative Systems, Database management, the Web and the IT branch in general.Andrea Ligioshttp://www.blogger.com/profile/16220024598996830928noreply@blogger.comBlogger11125tag:blogger.com,1999:blog-3110117530127187720.post-40311075021487658672012-10-26T16:36:00.000+02:002012-10-26T16:38:15.760+02:00Mapping SQL parameters with Spring's BeanPropertySqlParameterSourceIf you don't use Spring for your J2EE web application database connectivity, then you should really check it out.<br />
<br />
If, instead, you are already using, maybe you don't know a really nice object i discovered some time ago: the <b>BeanPropertySqlParameterSource</b><br />
<br />
The classic way to go in a query (or in an update) is to set the params one by one;<br />
let's take this Person bean as example (id field omitted in this example):<br />
<br />
<pre class="brush:java">
<br/>
public class Person {
private String name;
private String lastname;
private Date birthdate;
private Language motherTongue;
/* Getters and Setters */
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getLastname() {
return lastname;
}
public void setLastname(String lastname) {
this.lastname = lastname;
}
public Date getBirthdate() {
return birthdate;
}
public void setBirthdate(Date birthdate) {
this.birthdate = birthdate;
}
public Language getMotherTongue() {
return motherTongue;
}
public void setMotherTongue(Language motherTongue) {
this.motherTongue = motherTongue;
}
}
<br/>
</pre>
<br />
and the Language class referenced in it: <br />
<br />
<pre class="brush:java">
<br/>
class Language {
int id;
int description;
/* Getters and Setters */
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getDescription() {
return description;
}
public void setDescription(int description) {
this.description = description;
}
}
<br/>
</pre>
<br />
<br />
<br />
(with Language values like this: {[1 , English] , [2 , Italian], etc...}<br />
<br />
<br />
Let's say i've filled all the fields in the Person object, and i want to insert it into a database.<br />
Its instance is named "person". Normally i would go with:<br />
<br />
<pre class="brush:java">
<br/>
String sql = "INSERT INTO person_table " +
" ( name, lastname, birthday, id_language) " +
" VALUES " +
" ( :name, :lastname, :birthdate, :idLanguage) ";
MapSqlParameterSource params = new MapSqlParameterSource();
params.addValue("name", person.getName(), Types.VARCHAR);
params.addValue("lastname", person.getLastname(), Types.VARCHAR);
params.addValue("birthdate", person.getBirthdate(), Types.DATE);
params.addValue("idLanguage", person.getLanguage().getId(), Types.NUMERIC);
return namedJdbcTemplate.update(sql, params);
<br/>
</pre>
<br />
Imagine a big bean with a lot of fields, with every field passed manually... it's not really a good way to go.<br />
<br />
With the BeanPropertySqlParameterSource object instead, IF we are taking all the fields from one object (and his inner objects), we can do like this:<br />
<br />
<pre class="brush:java">
<br/>
String sql = "INSERT INTO person_table " +
" ( name, lastname, birthday, id_language) " +
" VALUES " +
" ( :name, :lastname, :birthdate, :language.id) ";
BeanPropertySqlParameterSource params = new BeanPropertySqlParameterSource(person);
return namedJdbcTemplate.update(sql, params);
<br/>
</pre>
<br />
Not bad uh ? It will retrieve the specified fields (and types) through Reflection, including the fields value in the inner objects contained in the main bean. As you can note, the notation is OGNL-like, with the getter of the inner object starting with a dot and a lower case letter, stripping "get" from the method name:<br />
<br />
<pre class="brush:java">
:language.id
</pre>
means
<br />
<pre class="brush:java">
person.getLanguage().getId();
</pre>
<br />
PRO:
<br />
<ul>
<li> less code;</li>
<li> auto-decoding field Type through Reflections, avoiding user errors;</li>
<li> changes on query or bean automatically managed, without the needs of change param settings</li>
</ul>
<br />
CONS:<br />
<ul>
<li> less customization possibilities</li>
</ul>
<br />
<br />
EnjoyAndrea Ligioshttp://www.blogger.com/profile/16220024598996830928noreply@blogger.com7tag:blogger.com,1999:blog-3110117530127187720.post-90987973947954982882012-09-12T12:22:00.000+02:002012-09-12T12:23:49.259+02:00HTML5 - Change the URL without reloading the pageWith HTML5 (compliant browsers), it is now possible to alter the URL in the address bar without reloading the page.<br />
This can be quite handy in many situations...<br />
to try it quickly, run this script after the page is loaded:<br />
<br />
<span style="color: yellow;"> window.history.pushState("","", "heyIJustChangedMyUrlWithoutPosting");</span><br />
<br />
To learn more, refer to:<br />
<br />
A) <a href="http://spoiledmilk.com/blog/html5-changing-the-browser-url-without-refreshing-page">The original article</a> ;<br />
<br />
B) <a href="http://stackoverflow.com/questions/3338642/updating-address-bar-with-new-url-without-hash-or-reloading-the-page">StackOverflow Post 1</a> and <a href="http://stackoverflow.com/questions/824349/modify-the-url-without-reloading-the-page">Post 2</a> ;<br />
<br />
C) <a href="http://www.w3.org/TR/html5-author/history.html#dom-history-pushstate">The Official W3C documentation</a>
.<br />
<br />
<br />
HTML5 is revealing its beauty every day a bit more!Andrea Ligioshttp://www.blogger.com/profile/16220024598996830928noreply@blogger.com16tag:blogger.com,1999:blog-3110117530127187720.post-11174297482476361842012-09-11T18:56:00.001+02:002012-09-12T12:21:26.821+02:00Art and technology: visit the Sistine Chapel and the Uffizi Gallery in a clickI visited this places for real, years ago, and believe me, they're absolutely stunning.<br />
But, if you are curious about them, and want to take a look without having to come to Italy,<br />
just visit this sites and enjoy ;)<br />
<br />
<br />
<a href="http://www.vatican.va/various/cappelle/sistina_vr/index.html" target="_blank">Sistine Chapel - 3D reproduction</a><br />
<br />
<br />
More on the Sistine Chapel: http://en.wikipedia.org/wiki/Sistine_Chapel<br />
<br />
<br />
<a href="http://www.vatican.va/various/cappelle/sistina_vr/index.html" target="_blank"></a>
<a href="http://www.vatican.va/various/cappelle/sistina_vr/index.html" target="_blank"></a>
<a href="http://www.haltadefinizione.com/magnifier.jsp?idopera=14&lingua=en" target="_blank">The Uffizi Gallery - visit everything !!</a><br />
<a href="http://www.vatican.va/various/cappelle/sistina_vr/index.html" target="_blank"></a><br />
<br />
More on the Uffizi Gallery: http://en.wikipedia.org/wiki/Uffizi<br />
<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://upload.wikimedia.org/wikipedia/commons/thumb/7/73/God2-Sistine_Chapel.png/400px-God2-Sistine_Chapel.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="151" src="http://upload.wikimedia.org/wikipedia/commons/thumb/7/73/God2-Sistine_Chapel.png/400px-God2-Sistine_Chapel.png" width="320" /></a></div>
<br />Andrea Ligioshttp://www.blogger.com/profile/16220024598996830928noreply@blogger.com0tag:blogger.com,1999:blog-3110117530127187720.post-17018978593168378802012-08-16T12:27:00.000+02:002012-08-16T15:06:00.774+02:00Struts2 - Write your own Interceptor for Session checking Interceptor Model is another great feature of Struts2.<br />
<br />
Interceptors are similar to Servlet Filters (actually, both of them are based on intercepting filter),but more configurable and powerful (note that they need to be Thread-Safe).<br />
<br />
<div style="color: yellow;">
SHORT STORY</div>
Basically, every request to an Action is filtered through an Interceptor Stack, a specific list of Interceptors for that specific Action.<br />
Any Interceptor performs some simple operations, then decides if the request can proceeds down the stack, to the next Interceptor (or to the Action, if we're on the last Interceptor), or it has to be redirected (back, or) somewhere else.<br />
When not specified, a request passes through the DEFAULT INTERCEPTOR STACK, that covers most of the needs we may have.<br />
But when our page has specific requirements, we can use a CUSTOM INTERCEPTOR STACK, made by a different number and kind of Interceptors (either available from Struts2 or, if the Interceptor with the logic that we want doesn't exist, created by ourselves).<br />
As Apache Wiki says, "Each and every Interceptor is pluggable, so you can decide exactly which features an Action needs to support."<br />
We can make all the custom stacks we want.<br />
Here is the list of the Interceptors https://cwiki.apache.org/WW/interceptors.html#Interceptors-TheDefaultConfiguration <br />
available on Struts2, and we can notice the Default Stack too, containing the majority of them.<br />
<br />
<br />
<div style="color: yellow;">
SCENARIO</div>
We want to perform a "Valid Session Checking" on our Actions, without writing any code into themselves or their configuration in Struts.xml.<br />
The goal is NOT to handle user authentication (totally another pair of shoes), but to ensure that our Session is "certified" (think about an Web Application loading data into Session at the beginning, and expecting to find that data in the Session later, during its whole life cycle...).<br />
<br />
<br />
<div style="color: yellow;">
WE NEED...</div>
one "entry-point" Action (setting up a "mock" Session Validity Token), under the control of Default Interceptor Stack;<br />
one Custom "SessionChecker" Interceptor;<br />
one Custom Interceptor Stack (containing all the default interceptors PLUS our sessionCheckerInterceptor);<br />
one Global Result "sessionExpired" on Struts.xml, redirecting to sessionExpired.jsp.<br />
<br />
<br />
<div style="color: yellow;">
LET'S START</div>
All of our Actions will extend a base Action called BaseAction:<br />
<br />
<pre class="brush:java">package com.andrealigios.struts2validation.presentation.action;
public abstract class BaseAction extends ActionSupport implements SessionAware, ServletResponseAware, ServletRequestAware{
private static final long serialVersionUID = 1L;
public final static String SESSION_VALIDATION_TOKEN_KEY = "SESSION_VALIDATION_TOKEN_KEY";
public final static boolean SESSION_VALIDATION_TOKEN_VALUE = true;
private HttpServletRequest req;
/* bla
bla
yada
yada */
public HttpServletRequest getServletRequest() {
return req;
}
}
</pre>
<br />
We need an entry-point Action (let's call it SessionSetupAction), to put a token in session; we will try to read it later from our Interceptor, <br />
to check if session is valid (if session is expired and replaced by a new one, our token won't be there);<br />
In the example, the token is a boolean; in real world applications, obviously we will use something different (generated, unique, salted, obfuscated, crypted stuff...), to prevent attacks and, for example, avoid a user to access the same session of a previous user on the same browser if not every windows/tabs were closed between the logout of the first user and the login of the second one (and the logout procedure didn't destroy properly the session).<br />
<br />
The SessionSetupAction must use the Default Interceptors Stack, because when we run this Action, we haven't put the token in the session yet, so we must bypass the sessionCheckerInterecptor this time.<br />
<br />
<pre class="brush:java"> package com.andrealigios.struts2validation.presentation.action;
public class SessionSetupAction extends BaseAction {
private static final long serialVersionUID = 1L;
public String execute() throws Exception {
try {
getServletRequest().getSession().setAttribute(SESSION_VALIDATION_TOKEN_KEY, true);
} catch (Exception e){
// Mock exception log...
e.printStackTrace();
}
return SUCCESS;
}
}
</pre>
<br />
<br />
Let's write the first (and only, in our example) of "n" business Actions for our Web Application. Let's call it FirstRealPageAction:<br />
<br />
<pre class="brush:java"> package com.andrealigios.struts2validation.presentation.action;
public class FirstRealPageAction extends BaseAction {
private static final long serialVersionUID = 1L;
public String execute() throws Exception {
try {
// Do business
// Do business
// Do business
return SUCCESS;
} catch (Exception e){
// Mock exception log...
e.printStackTrace();
return ERROR;
}
}
}
</pre>
<br />
<br />
<br />
<br />
Now it's the turn of the JSPs:<br />
<br />
<pre class="brush:xml"> <!-- firstRealPage.jsp -->
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>First Real Page</title>
</head>
<body>
<p>
This is one of the Web Application pages. <br/>
You are here, then the token is correctly loaded into the Session.
</p>
</body>
</html>
<!-- error.jsp -->
<%@ page isErrorPage="true" %>
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>Error</title>
</head>
<body>
<p>
An error occurred!
</p>
</body>
</html>
<!-- sessionExpired.jsp -->
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>Session Expired</title>
</head>
<body>
<p>
This is the Session Expired page.<br/>
You are here because: <br/>
1) Your session was properly set up, but is expired; <br/>
2) Your session was NOT properly set up (for example, you tried to reach this page directly by URL). <br/>
Press the button to pass through the sessionSetup Action and configure your Session properly.
</p>
<s:submit action="sessionSetup" value="Restart from the Entry point" />
</body>
</html>
</pre>
<br />
At this point, we only miss the configuration and the interceptor itself. The configuration, quite long but (i hope) well-commented, is the following:<br />
<br />
<pre class="brush:xml"><?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<package name="default-package" extends="struts-default">
<interceptors>
<!-- ========================================================== -->
<!-- DECLARATION OF THE DEFAULT INTERCEPTOR STACK -->
<!-- ========================================================== -->
<interceptor-stack name="defaultStack">
<interceptor-ref name="exception"/>
<interceptor-ref name="alias"/>
<interceptor-ref name="servletConfig"/>
<interceptor-ref name="i18n"/>
<interceptor-ref name="prepare"/>
<interceptor-ref name="chain"/>
<interceptor-ref name="scopedModelDriven"/>
<interceptor-ref name="modelDriven"/>
<interceptor-ref name="fileUpload"/>
<interceptor-ref name="checkbox"/>
<interceptor-ref name="multiselect"/>
<interceptor-ref name="staticParams"/>
<interceptor-ref name="actionMappingParams"/>
<interceptor-ref name="params">
<param name="excludeParams">dojo\..*,^struts\..*,^session\..*,^request\..*,^application\..*,^servlet(Request|Response)\..*,parameters\...*</param>
</interceptor-ref>
<interceptor-ref name="conversionError"/>
<interceptor-ref name="validation">
<param name="excludeMethods">input,back,cancel,browse</param>
</interceptor-ref>
<interceptor-ref name="workflow">
<param name="excludeMethods">input,back,cancel,browse</param>
</interceptor-ref>
<interceptor-ref name="debugging"/>
</interceptor-stack>
</interceptors>
<!-- ========================================================== -->
<!-- SIMPLE REDIRECT TO ENTRY POINT WHEN NO PAGE SPECIFIED -->
<!-- ========================================================== -->
<action name="welcome">
<result type="redirect-action">
<param name="actionName">sessionSetup</param>
</result>
</action>
<!-- ========================================================== -->
<!-- ENTRY POINT ACTION, UNDER DEFAULT INTERCEPTOR STACK -->
<!-- ========================================================== -->
<action name="sessionSetup" class="com.andrealigios.struts2validation.presentation.action.SessionSetupAction">
<result type="redirect-action">
<param name="actionName">firstRealPage</param>
</result>
</action>
</package>
<package name="our-custom-package" extends="struts-default">
<interceptors>
<!-- ========================================================== -->
<!-- DECLARATION OF OUR CUSTOM INTERCEPTOR FOR SESSION CHECKING -->
<!-- ========================================================== -->
<interceptor name="our-mock-session-checker-interceptor"
class="com.andrealigios.struts2validation.presentation.interceptor.MockSessionCheckerInterceptor"/>
<!-- ========================================================== -->
<!-- ============================================== -->
<!-- DECLARATION OF OUR CUSTOM INTERCEPTOR STACK, -->
<!-- THAT WILL USE OUR CUSTOM INTERCEPTOR -->
<!-- ============================================== -->
<interceptor-stack name="ourCustomStack">
<interceptor-ref name="exception"/>
<!-- ===================================== -->
<!-- WE PUT OUR INTERCEPTOR REFERENCE HERE -->
<!-- ===================================== -->
<interceptor-ref name="our-mock-session-checker-interceptor"/>
<!-- ===================================== -->
<interceptor-ref name="alias"/>
<interceptor-ref name="servletConfig"/>
<interceptor-ref name="i18n"/>
<interceptor-ref name="prepare"/>
<interceptor-ref name="chain"/>
<interceptor-ref name="scopedModelDriven"/>
<interceptor-ref name="modelDriven"/>
<interceptor-ref name="fileUpload"/>
<interceptor-ref name="checkbox"/>
<interceptor-ref name="multiselect"/>
<interceptor-ref name="staticParams"/>
<interceptor-ref name="actionMappingParams"/>
<interceptor-ref name="params">
<param name="excludeParams">dojo\..*,^struts\..*,^session\..*,^request\..*,^application\..*,^servlet(Request|Response)\..*,parameters\...*</param>
</interceptor-ref>
<interceptor-ref name="conversionError"/>
<interceptor-ref name="validation">
<param name="excludeMethods">input,back,cancel,browse</param>
</interceptor-ref>
<interceptor-ref name="workflow">
<param name="excludeMethods">input,back,cancel,browse</param>
</interceptor-ref>
<interceptor-ref name="debugging"/>
</interceptor-stack>
<!-- ============================================== -->
</interceptors>
<!-- ===================================================== -->
<!-- THE STACK WE WILL USE FOR THE ACTIONS OF THIS PACKAGE -->
<!-- ===================================================== -->
<default-interceptor-ref name="ourCustomStack" />
<!-- ===================================================== -->
<global-results>
<!-- ===================================================== -->
<!-- THE GLOBAL RESULT USED TO REDIRECT A REQUEST TO THE -->
<!-- "SESSION EXPIRED" PAGE FROM WITHIN OUR INTERCEPTOR -->
<!-- ===================================================== -->
<result name="sessionExpired">jsp/sessionExpired.jsp</result>
<!-- ===================================================== -->
</global-results>
<action name="firstRealPage" class="com.andrealigios.struts2validation.presentation.action.FirstRealPageAction">
<result name="success">jsp/firstRealPage.jsp</param>
<result name="error">jsp/error.jsp</param>
</action>
</package>
</struts>
</pre>
<br />
Got it ? When the Actions of the package "our-custom-package" get called, the request passes into our Interceptor, that will perform the check on the token. <br />
Note that it's possible to use different stacks into one package, instead of different packages.<br />
<br />
Final part, the one that brought you here: our custom Interceptor.<br />
<br />
<br />
<pre class="brush:java">package com.andrealigios.struts2validation.presentation.interceptor;
import javax.servlet.http.HttpServletRequest;
import org.apache.struts2.StrutsStatics;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.AbstractInterceptor;
public class MockSessionCheckerInterceptor extends AbstractInterceptor implements Constants{
private static final long serialVersionUID = 1L;
public String intercept(ActionInvocation invocation) throws Exception {
final ActionContext context = invocation.getInvocationContext();
try {
HttpServletRequest request = (HttpServletRequest) context.get(StrutsStatics.HTTP_REQUEST);
String sessionToken = (String) request.getSession().getAttribute(SESSION_VALIDATION_TOKEN_KEY);
if (sessionToken==null || !sessionToken.equals(SESSION_VALIDATION_TOKEN_VALUE))
return "sessionExpired";
/* If the session Token is not there (or is different from what we want),
uses the Global Result "sessionExpired" to redirect the request
to a page that will notify the user, giving him the opportunity to
restart the Session in the right way.
*/
/* Else, everything is fine, let's proceed down the stack ! */
return invocation.invoke();
} catch (Exception e){
e.printStackTrace();
return ERROR; // not defined in our example...
}
}
</pre>
<br />
<br />
<br />
The coding is over. <br />
If you set up your simple Web Application like this, and try to access directly your "firstRealPage", you should be redirected to the sessionExpired page; the same thing should happen if you stay too much in the "firstRealPage" page and try to reload it...<br />
<br />
Despite the messy exposition, I hope you enjoyed this ;)Andrea Ligioshttp://www.blogger.com/profile/16220024598996830928noreply@blogger.com0tag:blogger.com,1999:blog-3110117530127187720.post-52923815619859182202012-06-12T11:36:00.004+02:002012-08-16T12:04:39.757+02:00JMS Queue in a Clustered EnvironmentUsing JMS queues is quite simple once set up correctly.<br />
<br />
But the simpleness immediately disappears when working in a clustered environment, whichever technology you are using; often you'll get <i>NameNotFoundException</i> or <span class="postbody"><i>JMSException: Connection not found</i> while trying to contact the JMS server.</span><br />
<br />
<span class="postbody">The reasons can be many, but most of the time the problem is in the JNDI Lookup procedure: in a clustered environment, is important to preserve <b>uniqueness of server names</b>, to avoid the needs of complex tricks and the appearance of weird and mysterious errors.</span><br />
<span class="postbody"><br /></span>
<span class="postbody">A <a href="http://www-304.ibm.com/support/docview.wss?rs=180&context=SSEQTP&dc=DB520&dc=D600%20%20&dc=DB530&dc=D700&dc=DB500&dc=DB540&dc=DB5%2010&dc=DB550&q1=%22objectName%22+%20not+found&uid=swg21201631&loc=en_US&cs=utf-8&lang=en" target="_blank">Technote from IBM</a> explains that, in a Websphere </span>clustered environment<br />
<blockquote class="tr_bq">
<i>"...in which 2
nodes have Application Servers with the same name, an attempt to lookup
an EJB in the name space during an EJB invocation finds the wrong
server.</i><br />
<i>[...]</i><br />
<i><b><span style="color: lime;">The solution for this problem is to have different server names for the servers for 2 different nodes.</span></b>" </i></blockquote>
<br />
This happens because both Application Servers on different nodes have same name (server1 in the example), and trying to lookup server1 on ejb node from application node will instead return the server1 of application node, the local one.<br />
<br />
The same thing happens on Weblogic, as they states in <a href="http://docs.oracle.com/cd/E13222_01/wls/docs103/pdf/jms_admin.pdf" target="_blank">JMS Admin documentation</a> (page 55):<br />
<br />
<blockquote class="tr_bq">
"Note:<i> JMS clients depend on unique WebLogic Server names to successfully access a cluster —</i> <i>even when WebLogic Servers reside in different domains. <b><span style="color: lime;">Therefore, make sure that all WebLogic Servers that JMS clients contact have unique server names.</span></b></i>"</blockquote>
<br />
Different server names, correct Lookups. That's all folks.<br />
<br />
Hope that helps ;)Andrea Ligioshttp://www.blogger.com/profile/16220024598996830928noreply@blogger.com0tag:blogger.com,1999:blog-3110117530127187720.post-63230824245125871002012-06-04T15:09:00.000+02:002012-09-12T15:57:52.474+02:00Struts2 XML VALIDATIONStruts 2 framework allows powerful and easy validation through Java, XML or Annotations.<br />
The first way (override of Validate() method) should be avoided.<br />
Both XML and Annotations are good ways to go, but as usual they're poorly documented / exampled.<br />
Let's take a look at XML Validation:
<br />
<br />
<br />
<b><span style="color: yellow;">STANDARD XML VALIDATION</span></b>
<br />
<br />
Scenario:<br />
You have a JSP that posts a form with new data (let's say one record about a person) to be inserted, to an Action named InsertOneRowAction.java.<br />
Data posted are: Name and Birthday (KISS paradigm ftw...).<br />
Fields in JSP page are named person.name and person.birthday (getters of Person.java value object).<br />
Both fields are mandatory, and Birthday must be a valid date between 1990 and 2000.<br />
<br />
Solution:<br />
Create a file with the name of the action and append "-Validation.xml" at the end, in the same package of the action.<br />
We will see then:<br />
com.andrealigios.struts2validation.presentation.action.<span style="color: lime;">InsertOneRowAction.java</span>
<br />
com.andrealigios.struts2validation.presentation.action.<span style="color: lime;">InsertOneRowAction-Validation.xml</span><br />
<br />
<br />
Open XML validation file and write the desired rules in there, in this case:<br />
<br />
<pre class="brush:xml"> <field name="person.name">
<field-validator type="requiredstring">
<message><![CDATA[ Name is mandatory ]]></message>
</field-validator>
</field>
<field name="person.birthday">
<field-validator type="required">
<message><![CDATA[ Birthday is mandatory ]]></message>
</field-validator>
<field-validator type="date">
<param name="min">01/01/1990</param>
<param name="max">01/01/2000</param>
<message><![CDATA[ Birthday must be a valid date between ${min} and ${max} ]]></message>
</field-validator>
</field>
</pre>
<br />
<br />
<br />
Now put the tag <fielderrors/> into your JSP.<br />
Done!<br />
<br />
Every time your data posted won't match the validation criteria, the Validation Interceptor will redirect your post to the source JSP page, showing the error messages and preventing wrong / incomplete data to reach the action's setters.
<br />
<br />
<br />
<b><span style="color: yellow;">DYNAMIC XML VALIDATION - FIELDEXPRESSION VALIDATOR</span></b>
<br />
<br />
Let's dig a bit deeper: we need min and max Date parameters to be dynamic, for example read from a database or a configuration file.<br />
We need to create a getter for each parameter, that does its business inside, and returns the desired java.util.Date.<br />
We can put the getter into the Action, or into Actions that are extended by ours (let's say BaseAction). Remind to preserve getters's visibility ;)<br />
<br />
<pre class="brush: java">
public Date getMinDateDynamicallyRead(){
// Do business
return minDate;
}
public Date getMaxDateDynamicallyRead(){
// Do business
return maxDate;
}
</pre>
<br />
<br />
Now, let's modify XML Validation file as follows:<br />
<br />
<pre class="brush:xml"> <field name="person.birthday">
<field-validator type="required">
<message><![CDATA[ Birthday is mandatory ]]></message>
</field-validator>
<field-validator type="date">
<message><![CDATA[ Birthday must be a valid date ]]></message>
</field-validator>
<field-validator type="fieldexpression">
<param name="expression">
<![CDATA[ person.birthday==null || (person.birthday >= minDateDynamicallyRead && person.birthday <= maxDateDynamicallyRead) ]]>
</param>
<message><![CDATA[ Birthday must be between ${minDateDynamicallyRead} and ${maxDateDynamicallyRead} ]]></message>
</field-validator>
</field>
</pre>
<br />
<br />
<br />
The <b>person.birthday == null</b> part is necessary to avoid redundancy of messages. If date is not provided, then the "required" validator must raise the error message, but there's no need to show that date is not inside our desired interval too...<br />
<br />
Note that you can chain multiple "fieldexpression" validators for a single field, performing different controls and showing different messages.<br />
<br />
<br />
<b><span style="color: yellow;">MULTIPLE ROWS XML VALIDATION - VISITOR VALIDATOR</span></b>
<br />
<br />
Now let's understand the most powerful, less documented feature of Struts 2 Validation : VISITOR validation.
<br />
<br />
Scenario:<br />
We are on a different JSP page, that shows us a list of persons, and allows us to perform multiple changes to their Names and Birthdays, and to save 'em all with one click on the UPDATE button.<br />
The main object into the JSP is PersonsTable.java (exposed through a getter from the Action), that exposes a list of Person.java objects, cycled within an iterator in the JSP:<br />
<br />
<br />
<pre class="brush:java">public List<Person> getPersonsFromDatabase(){
return savedPersons;
}
</pre>
<br />
The JSP UPDATE button posts to an Action named UpdateMultipleRowsAction.java
<br />
(We obviously can't validate the persons in the previous way, because we can't (and definitely don't want to) write a huge, static XML file specifying row indexes ;)<br />
<br />
Struts2 allows us to resolve the problem like follows:<br />
<br />
Create the usual XML Validation file in action's package, beside the action:<br />
com.andrealigios.struts2validation.presentation.action.<span style="color: lime;">UpdateMultipleRowsAction.java</span><br />
com.andrealigios.struts2validation.presentation.action.<span style="color: lime;">UpdateMultipleRowsAction-Validation.xml</span>
<br />
<br />
inside the xml file, let's write:<br />
<br />
<pre class="brush:xml"><validators>
<field name="personsTable.personsFromDatabase">
<field-validator type="visitor">
<message></message>
</field-validator>
</field>
</validators>
</pre>
<br />
<br />
This command will tell Struts to perform, for EVERY single row, the validation using the validation rules of the object listed (in this case, Person.java).<br />
The real validation must be written into an xml file <u>UNDER THE OBJECT PACKAGE</u> (<b>NOT</b> the Actions package),
with a name composed by the Object class concatenated to "-Validation.xml".<br />
<br />
For example:<br />
com.andrealigios.struts2validation.valueobject.<span style="color: lime;">Person.java</span><br />
com.andrealigios.struts2validation.valueobject.<span style="color: lime;">Person-Validation.xml</span><br />
<br />
Inside Person-Validation.xml, we can put the same rules used in previous examples, pointing to the name of the field (name of the getters of Person.java, in this case "name" or "birthday", without "person" or other stuff before), as follows:<br />
<br />
<br />
<pre class="brush:xml"> <field name="name">
<field-validator type="requiredstring">
<message><![CDATA[ Name is mandatory ]]></message>
</field-validator>
</field>
<field name="birthday">
<field-validator type="required">
<message><![CDATA[ Birthday is mandatory ]]></message>
</field-validator>
<field-validator type="date">
<param name="min">01/01/1990</param>
<param name="max">01/01/2000</param>
<message><![CDATA[ Birthday must be a valid date between ${min} and ${max} ]]></message>
</field-validator>
</field>
</pre>
<br />
<br />
Enjoy, forgive my Engrish and... comment!! ;)<br />
<br />
<br />
<br />Andrea Ligioshttp://www.blogger.com/profile/16220024598996830928noreply@blogger.com3tag:blogger.com,1999:blog-3110117530127187720.post-43450223917058127312011-08-16T12:13:00.001+02:002012-08-16T12:04:16.177+02:00ARENAS, a Doom and Quake Movie<div class="separator" style="clear: both; text-align: center;">
<a href="http://quake-rebellion.com/includes/php/thumb_format.php?thumb=http://www.quake-rebellion.com/includes/php/4x3fix.php?src=http://img.youtube.com/vi/L9Iyhh0FTWw/0.jpg" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img border="0" height="126" src="http://quake-rebellion.com/includes/php/thumb_format.php?thumb=http://www.quake-rebellion.com/includes/php/4x3fix.php?src=http://img.youtube.com/vi/L9Iyhh0FTWw/0.jpg" width="222" /></a></div>
<br />
<a href="http://quake-rebellion.com/">Visit the Official site</a><br />
<br />
Doesn't matter if you play ID Software's games since the first Doom in 1993 or if you tried QuakeLive for the first time some days ago: if you like Doom and Quake worlds, you MUST see ARENAS ! <br />
And the more you know those worlds, the more you will find absolutely great this work: it takes charachters, artifacts, sounds and maps from Doom, Doom 3, Quake, Quake II, Quake III Arena, Quake IV and QuakeLive, to compose a 70 minutes movie with a charming and coherent story and an amazing animation.<br />
<br />
Simply the best fanmade movie ever seen (even better than Dead Fantasy, imho).<br />
<br />
Available on YouTube in 11 high-definition single episodes (from 0 to 10), with intro and credits on each one, or in a 480p-70mins whole movie (named "episode 11"), with some deleted footage mixed in.<br />
<br />
A BIG thanks to the director Joe Goss and to all the people who worked on it for bringing this pearl to life.<br />
<br />
God Speed guys, <br />
<br />
<b>IDDQD</b>.<br />
<br />
<br />
<a href="http://www.youtube.com/watch?v=L9Iyhh0FTWw">Watch Arenas full movie (480p) </a><br />
<br />
<a href="http://www.youtube.com/watch?v=sjj-m3yuo70">Watch Arenas "Broken Bridge" (extra movie)</a><br />
<br />
<a href="http://www.youtube.com/watch?v=rjf2gc1QNbk">Watch Arenas Season II Trailer</a><br />
<br />
or watch single episodes in HD from the Official Sites (all episodes in 720p, episodes 0,8,9,10 in 1080p too): <a href="http://quake-rebellion.com/?page=episodes">EPISODES LIST</a><br />
<br />
<br />
<br />
Enjoy! =]<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://www.quake-rebellion.com/images/thumbs/pre_rangersarge.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="270" src="http://www.quake-rebellion.com/images/thumbs/pre_rangersarge.jpg" width="480" /></a></div>
Andrea Ligioshttp://www.blogger.com/profile/16220024598996830928noreply@blogger.com0tag:blogger.com,1999:blog-3110117530127187720.post-38232899827769708362011-07-01T17:55:00.000+02:002012-08-16T12:04:05.077+02:00The best Text Editor ?...once was UltraEdit, no doubt. But you had to pay it sixty bucks.<br />
Then PSPad came. And it was (is) freeware and very good.<br />
But the excellence was reach when a GPL, open source product saw the light: Notepad ++. <br />
Today, lots of people are using N++, considering it the best free text editor for Windows out now, and i guess they're right.<br />
<br />
This comparison will show you why: <a href="http://text-editors.findthebest.com/compare/9-26-36/Notepad-vs-UltraEdit-vs-PSPad"></a><br />
<br />
Notepad++ has code folding, automatic multiple highlighting, column mode, a very rich choice of languages for code-coloring, can compare files in an elegant way, and so on... in addition, it uses a very low amount of memory to work, and it's fast like hell.<br />
<br />
Downloading the hex plugin (just copy it into your "Program Files\Notepad++\Plugins" folder), it can hex-edit too (Article on Hex Editor Plugin: <a href="http://www.mydigitallife.info/use-notepad-as-hex-editor-with-plugin-download-free-winhex-alternative/"></a> )<br />
<br />
Let's give it a try, i don't think you will ever come back ;)<br />
<br />
Official Notepad++ site: <a href="http://notepad-plus-plus.org/"></a><br />
<br />
P.S: It lacks of FTP / SSH connectivity, but do you really need such kind of things on a text editor ? Filezilla is there for that ;)<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://www.informaticamica.com/images/stories/notepadpp.png" imageanchor="1"><img border="0" height="128" src="http://www.informaticamica.com/images/stories/notepadpp.png" width="128" /></a></div>
Andrea Ligioshttp://www.blogger.com/profile/16220024598996830928noreply@blogger.com2tag:blogger.com,1999:blog-3110117530127187720.post-30176722735621864282010-11-30T10:03:00.000+01:002012-08-16T12:05:58.206+02:00How to record a song played by your PCIf you are looking for something like the Windows <a href="http://en.wikipedia.org/wiki/Sound_Recorder_%28Windows%29">Sound Recorder</a> but more professional AND freeware, you are in the right place.<br />
<br />
The best easy-to-use but nevertheless quite professional software out there is <a href="http://audacity.sourceforge.net/">Audacity</a>.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://www.easyeartraining.com/wp-content/uploads/2010/11/Audacity_Icon_fb.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://www.easyeartraining.com/wp-content/uploads/2010/11/Audacity_Icon_fb.png" /></a></div>
<br />
<br />
It is cross-platform, that means that it's not OS-dependent: you can get a version of it for Windows, Mac OS X and GNU/Linux too.<br />
<br />
Believe me, unless you have very unusual needs, you'll be ready to get started immediately:<br />
press REC to record, STOP to end the task, SAVE the project or EXPORT him as MP3, WAV or Ogg Vorbis. End of story.<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://www.webcooltips.com/wp-content/uploads/2010/02/audacity_karaoke.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://www.webcooltips.com/wp-content/uploads/2010/02/audacity_karaoke.PNG" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
<br />
<br />
Once you get more confident with it, you can do a lot of stuff without having special skills: <br />
<ul>
<li>record multiple tracks (automatically, it creates a new track everytime you press Stop and Rec again), that you'll be able to mix down in a single one or to manage individually, according to your needs;</li>
<li>apply tons of effects (fade in/fade out, filters, normalize, reverse,..); </li>
<li>set ID3 Tags (meta-informations about Author, Title, Year, Genre,..) </li>
<li>synchronize tracks, generate noise and so on... </li>
</ul>
As usual for the open-source products, you can find a <a href="http://audacity.sourceforge.net/manual-1.2/">good guide</a> for free too ;)<br />
<br />
<br />
<br />
<br />
NOTE: If your system is playing, but your Audacity is NOT recording, you should check your INPUT AUDIO SETTINGS, and verify that Recording Control and Stereo Mix are both enabled. <br />
<br />
To open Input Audio Settings: <br />
<ul>
<li>Open your "Master Volume" window by double-clicking the speaker icon in the System Tray area (or through the Control Panel, Sound and Audio Device Properties, Volume, Advanced tab);</li>
<li>Click on Options menu, select Properties, drop down the combobox and select your Input peripheral (every soundcard has integrated an input and an output device);</li>
<li>Click ok and adjust the settings until they're good (recording volume affects the quality of the registration, so raise or lower it to an appropriate value).</li>
</ul>
Andrea Ligioshttp://www.blogger.com/profile/16220024598996830928noreply@blogger.com0tag:blogger.com,1999:blog-3110117530127187720.post-30127408750565243402010-11-17T15:39:00.015+01:002012-08-16T12:06:32.636+02:00Export and Import data in Oracle (including BLOB and XMLTYPE fields)To copy data from one database schema to another: that's the point. <br />
You never needed it before ? You will, no doubt. And at that moment, you'll find very handy the wizard functionality of Toad (or similar softwares).<br />
To use it, just right-click on the desired table in the left column, select "Copy data to another schema" from the context menù and follow the wizard. <br />
You will be asked to select a destination connection (aka database), a destination user (aka schema), then to select the options you want to set: commit frequency (default is every 500 rows), append or truncate (delete+insert) mode, "Where" conditions and so on. Then press "Execute" and wait for the script to end. <br />
Note that you can move data from a user to another user on the same database, or between different users of different databases too.<br />
Great, you moved some data from one table to one identical table on a different place.<br />
<br />
But wait... what if you have some <b>BLOB </b>or <b>XMLTYPE </b>fields ? They are very complex, huge fields, containing tons of bytes and they are not managed by Toad or by any other wizard I know (except for some expensive 3rd party tools that you need to buy just to do this single operation). <br />
<br />
This is the interesting part of the post: Oracle itself provides some tools to export and import ANY kind of data without any problems.<br />
<br />
These tools are <b>EXP </b>and <b>IMP</b> and they are two command-line executables given to you by the Oracle (Client, Server, doesn't matter) installer, if you choose to install Oracle Tools too. You can find them in the <b>BIN </b>folder, under your Oracle folder.<br />
<br />
Their behavior is well explained in the <a href="http://www.orafaq.com/wiki/Import_Export_FAQ">Oracle Wiki</a>, but let's see how to use them in a few seconds ;)<br />
Remember to write all that stuff in a single row, I'm putting it on different lines to keep the code clean.<br />
<br />
<blockquote>
<div style="color: orange;">
<b>EXP syntax for dummies</b></div>
<br />
<b><span style="color: lime;">EXP</span> </b><span style="color: yellow;">username</span>/<span style="color: yellow;">password</span>@<span style="color: yellow;">dbInstance</span><br />
<b style="color: lime;">file</b>=<span style="color: yellow;">dumpPath</span>\<span style="color: yellow;">dumpFilename</span><br />
<b style="color: lime;">log</b>=<span style="color: yellow;">logPath</span>\<span style="color: yellow;">logFilename</span><br />
<b style="color: lime;">tables</b>=<span style="color: yellow;">table1</span>,<span style="color: yellow;">table2</span>,...<span style="color: yellow;">tableN</span><br />
<b style="color: lime;">query</b>=\" <span style="color: yellow;">put_here_your_optional_query_conditions</span> \"</blockquote>
<br />
<br />
<span style="color: #6fa8dc;">Example n.1:</span> export the hypothetical tables EMPLOYEES, CUSTOMERS and DOCUMENTS from a user "MySociety", password "myPass", located in a database mapped as "my_database" on tnsnames.ora, without where conditions, in the folder "myexports" located on C: drive in the file system, with subfolders for dumps and logs.<br />
<br />
EXP MySociety/myPass@my_database file=C:\myexports\dmp\allthebigtables.dmp log=C:\myexports\logs\allthebigtables.txt tables=EMPLOYEES, CUSTOMERS, DOCUMENTS<br />
<br />
<br />
<br />
<span style="color: #6fa8dc;">Example n.2:</span> export the same tables EMPLOYEES and CUSTOMERS, specifying that the field AGE of both must be over 50.<br />
<br />
EXP MySociety/myPass@my_database file=C:\myexports\dmp\overFifty.dmp log=C:\myexports\logs\overFifty.txt tables=EMPLOYEES, CUSTOMERS query=\" where age>50 \"<br />
<br />
<br />
<br />
<span style="color: #6fa8dc;">Example n.3:</span> export the same tables EMPLOYEES and CUSTOMERS, specifying that the field AGE of EMPLOYEES must be over 50, and the field AGE of CUSTOMERS must be under 25. You will need to write two rows and execute them consecutively to do that.<br />
<br />
EXP MySociety/myPass@my_database file=C:\myexports\dmp\empOverFifty.dmp log=C:\myexports\logs\empOverFifty.txt tables=EMPLOYEES query=\" where age>50 \"<br />
<br />
EXP MySociety/myPass@my_database file=C:\myexports\dmp\customerUnderTwentfive.dmp log=C:\myexports\logs\customerUnderTwentfive.txt tables=CUSTOMERS query=\" where age<25 \"<br />
<br />
<br />
<br />
This script is so powerfull that it will read and export all the data from all the tables targeted in one single dump file. Now let's see how to import that data in a different database and schema.<br />
<br />
<br />
<blockquote>
<div style="color: orange;">
<b>IMP Syntax for dummies</b></div>
<br />
<b style="color: lime;">IMP</b> <span style="color: yellow;">username</span>/<span style="color: yellow;">password</span>@<span style="color: yellow;">dbInstance</span><br />
<b style="color: lime;">file</b>=<span style="color: yellow;">dumpPath</span>\<span style="color: yellow;">dumpFilename</span><br />
<b style="color: lime;">log</b>=<span style="color: yellow;">logPath</span>\<span style="color: yellow;">logFilename</span><br />
<b style="color: lime;">tables</b>=<span style="color: yellow;">table1</span>,<span style="color: yellow;">table2</span>,...<span style="color: yellow;">tableN</span><br />
<b style="color: lime;">ignore</b>=<span style="color: yellow;">Y</span></blockquote>
<br />
<br />
<span style="color: #6fa8dc;">Example:</span> we want to import the previously saved "allthebigtables.dmp" file into a database called "external_database", in a schema with username "YourSociety" and passowrd "yourPass"<br />
<br />
<br />
IMP YourSociety/yourPass@external_database file=C:\myexports\dmp\allthebigtables.dmp log=C:\myexports\logs\allthebigtables_import.txt tables=EMPLOYEES, CUSTOMERS, DOCUMENTS ignore=Y<br />
<br />
<br />
This will import all the data previously exported into the new database, ignoring possible constraints problems.<br />
<br />
Now you are ready to backup or move all your data between all your database instances and schemas. <br />
<br />
As a further step, it's a very good idea to create batch files to achieve this, especially if you think that the operation you are performing now will be repeated in the future (like moving data between different environments, for example from Production to Test).<br />
<br />
To do this, open a text editor, create a new text file, put all the stuff you've written into it, and save it with the ".bat" extension. <br />
To avoid confusion, I recommend to use at least one file for exports and another one for imports ;)<br />
P.S: Remember to use full path before IMP or EXP, to be able to launch the batch file from any place on your system, without touching environment variables like PATH and CLASSPATH.<br />
<br />
Feel free to comment if you find this post useful :)<br />
<br />
Special thanks to Giovanni Galasso for the support.Andrea Ligioshttp://www.blogger.com/profile/16220024598996830928noreply@blogger.com1tag:blogger.com,1999:blog-3110117530127187720.post-10063299209615766882010-11-16T17:42:00.000+01:002012-08-16T12:06:53.865+02:00Oracle fundamentalsIf you are approaching Oracle (one of the biggest and widely used databases on the market) for the first time, there are some little hints you should (or, better, you MUST) know to start working on it.<br />
<br />
First of all, you have to install an Oracle Client on your machine, in order to access your database(s).<br />
No problems here, just launch the installer and press Next until it's over.<br />
<br />
The second step is to add your specific database parameters to the Oracle configuration file, called <b>TNSNAMES.ORA</b>, and located under the path <b><i>YOUR_ORACLE_DIRECTORY</i>\network\ADMIN</b>.<br />
<br />
NOTE: Every time you will need to access a different Oracle database for the first time, you will need to add the data into this file, so it's importat to remember its name and location, and its configuration rules. <br />
<br />
<br />
<br />
The configuration is quite simple, and well described in the <a href="http://www.orafaq.com/wiki/Tnsnames.ora">Oracle Wiki</a>:<br />
<br />
<blockquote>
# COMMENTS DESCRIBING THE INSTANCE BELOW (comments starts with # symbol)<br />
<<span style="color: yellow;">addressname</span>> =<br />
<br />
(DESCRIPTION =<br />
(ADDRESS_LIST =<br />
(ADDRESS = (PROTOCOL = TCP)<br />
(Host = <<span style="color: yellow;">hostname</span>>)<br />
(Port = <<span style="color: yellow;">port</span>>))<br />
)<br />
(CONNECT_DATA =<br />
(SERVICE_NAME = <<span style="color: yellow;">sid</span>>)<br />
)<br />
) </blockquote>
<br />
<br />
where <i>addressname</i> is the name of your database instance (i.e. MY_DATABASE), <i>hostname</i> is the physical address (i.e. "127.0.0.1" or "mydb.mydomain.com"), <i>port</i> is the port where database is listening (i.e. 1521), and <i>sid</i> is the System ID (i.e. MY_SID).<br />
<br />
Note: When you will be asked to provide a Connection String to your database, that string will be composed in the form:<br />
<br />
<blockquote>
hostname<b>:</b>port<b>:</b>sid</blockquote>
, for example <i><b>mydb.mydomain.com:1521:MY_SID</b></i><br />
<br />
You will need that string every time you'll want to connect to your database directly through a software that doesn't depends on your computer settings, for example a Java web application (that runs on a server, not on your pc). In that case, according to your specific Application Server peculiarity, connection string will be something like <i>jdbc:oracle:thin:@mydb.mydomain.com:1521:MY_SID.</i><br />
<br />
But everytime you will need to connect to your database from your computer, through an utility like <i>SQL*Plus</i> or Quest Software's <i>TOAD</i>, you will refer the database using only its <i>addressname</i>, keeping the things easy, no need to remember stuff like port, sid or hostname, and no need to reopen the tnsnames.ora every time... just remember your addressname (MY_DATABASE in the example above).<br />
<br />
<br />
<br />
Now you are ready to connect to your database.<br />
Open the <b>SQL*Plus</b> utility, installed together with the Oracle Client and located (in Windows and by default installation) under the Programs Menu -> Oracle Folder -> Application Development.<br />
<br />
It will ask you to enter the USERNAME, the PASSWORD and the HOSSTRING (addressname): enter them and press OK. <br />
Congratulations, you are now browsing your database.<br />
<br />
SQL*Plus has an owner syntax, you can find the details <a href="http://ss64.com/ora/syntax-sqlplus.html">here</a>; however, the simplest way to query your database is:<br />
<ol>
<li>Write your SQL statement, pressing Enter everytime you need a new line to keep the code clean (don't worry, it won't run);</li>
<li>Press Enter once again on the last, empty row: this will end the command;</li>
<li>Write RUN to execute the SQL statement (just recorded) in the SQL*Plus buffer.</li>
</ol>
Enjoy. <br />
<br />
Note that SQL*Plus is an important, fast and powerfull tool, but you will use it mainly for debugging and forensic purposes.<br />
It's very likely that you will be provided with a 3rd party softwares like <a href="http://www.quest.com/toad/">TOAD</a> or one of its freeware alternatives (take a look at Oracle's <a href="http://www.oracle.com/technetwork/developer-tools/sql-developer/overview/index.html">SQL Developer</a>, <a href="http://sqltools.net/index.html">SQLTools</a> or <a href="http://torasql.com/">TOra</a>, the last available for linux too) in order to manage your database in a productive way.Andrea Ligioshttp://www.blogger.com/profile/16220024598996830928noreply@blogger.com0