Kategori arşivi: Püf Noktası

IDL Compiler

Bu aralar bir Corba projesinde çalışıyorum. Corba teknolojisinde interface tanımlama dili olarak IDL kullanılıyor. Eğer bir Corba servisine erişimi sağlamak için size bir IDL verildi ise, bu IDL´i kullanarak client sınıflarını oluşturabilirsiniz. Bu işlem için aşağidaki sınıfı oluşturdum.

Bu implementasyon Sun JDK bünyesinde bulunan com.sun.tools.corba.se.idl.toJavaPortable.Compile ve IBM JDK bünyesinde bulunan com.ibm.idl.toJavaPortable.Compile sınıflarını kullanarak client sınıflarını oluşturuyor. Bu iki sınıf JDK lib dizini altındaki tools.jar içinde yer alıyor. Sınıfın bulunabilmesi için -Djava.home= parametresinin tanımlanmasi gerekiyor. JDK bin dizininde yer alan idlj komutu ile aynı işlemi gerçekleştirebilirsiniz.

[sourcecode language=’java’]
/**
*
* @author F520947 Oezcan Acar
*
*/
public interface IdlToJavaGeneratorService
{
void generate( String workingDir, String idlFile, String outputDirectroy );
}

[/sourcecode]

[sourcecode language=’java’]
import java.io.File;
import java.io.PrintStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.List;

import org.codehaus.plexus.util.StringOutputStream;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.csg.cs.testing.common.CommonConstants;
import com.csg.cs.testing.generator.GeneratorException;
import com.csg.cs.testing.project.ProjectDirectoryUtils;
import com.csg.cs.testing.service.logger.ILogger;

/**
*
* @author F520947 Oezcan Acar
*
*/
@Component
final class DefaultIdlToJavaGeneratorServiceImpl implements IdlToJavaGeneratorService
{

@Autowired
private ILogger logger;

@Autowired
private IIdlParserService parser;

@Override
public void generate( final String inputDirectory, final String idlFile, final String outputDirectory)
{

File idl = new File( ProjectDirectoryUtils.getWorkingDirRoot() + “/” + idlFile );
List< String > includes = parser.getIdlIncludes( idl );

List args = new ArrayList< String >();

args.add( “-emitAll” );
args.add( “-fclient” );
args.add( “-pkgPrefix” );

String interfaceName = parser.getRealInterface( idl );
args.add( interfaceName );
args.add( CommonConstants.SERVICE_PACKAGE );

for (String include : includes)
{
args.add( “-pkgPrefix” );
args.add( include );
args.add( CommonConstants.SERVICE_PACKAGE );
}

List< String > baseInterfaces = parser.getBaseInterfaces( idl );
for (String base : baseInterfaces)
{
args.add( “-pkgPrefix” );
args.add( base );
args.add( CommonConstants.SERVICE_PACKAGE );
}

args.add( “-pkgPrefix” );
args.add( “CS_Inquiry_0_0” );
args.add( CommonConstants.SERVICE_PACKAGE );

args.add( “-pkgPrefix” );
args.add( “CS_Inquiry_0_0Operations” );
args.add( CommonConstants.SERVICE_PACKAGE );

args.add( “-i” );
args.add( inputDirectory );
args.add( “-td” );
args.add( outputDirectory );
args.add( idlFile );

Class compilerClass = getCompilerClass();
invokeCompiler( compilerClass, args );
}

private Class getCompilerClass()
{
ClassLoader cl = this.getClass().getClassLoader();
Class idljCompiler;
try
{
idljCompiler = Class.forName(getIDLCompilerClass());
}
catch (ClassNotFoundException e)
{
try
{
File javaHome = new File(System.getProperty(“java.home”));
logger.log(“java.home=” + javaHome);
logger.log(“tools.har=” + javaHome+”/lib/tools.jar”);

File toolsJar = new File(javaHome, “/lib/tools.jar”);
URL toolsJarUrl = toolsJar.toURL();
URLClassLoader urlLoader = new URLClassLoader(new URL[]
{ toolsJarUrl }, cl);

// Unfortunately the idlj compiler reads messages using the
// system class path.
// Therefore this really nasty hack is required.
System.setProperty(“java.class.path”,System.getProperty(“java.class.path”)+ System.getProperty(“path.separator”)+ toolsJar.getAbsolutePath());
if (System.getProperty(“java.vm.name”).indexOf(“HotSpot”) != -1)
{
urlLoader.loadClass(“com.sun.tools.corba.se.idl.som.cff.FileLocator”);
}
idljCompiler = urlLoader.loadClass(getIDLCompilerClass());
}
catch (Exception notUsed)
{
throw new GeneratorException(notUsed);
}
}
return idljCompiler;
}

private String getIDLCompilerClass()
{
String vendor = System.getProperty( “java.vm.vendor” );

if ( vendor.indexOf( “IBM” ) != -1 )
{
return “com.ibm.idl.toJavaPortable.Compile”;
}
return “com.sun.tools.corba.se.idl.toJavaPortable.Compile”;
}

private void invokeCompiler( final Class compilerClass, final List args)
{
logger.log(“Current dir : ” + System.getProperty(“user.dir”));
Method compilerMainMethod;
String arguments[];

args.add(0, “-verbose”);
arguments = (String[]) args.toArray(new String[args.size()]);
String command = compilerClass.getName();
for (int i = 0; i < arguments.length; i++) { command += " " + arguments[i]; } logger.log(command); try { compilerMainMethod = compilerClass.getMethod("main", new Class[] { String[].class }); } catch (NoSuchMethodException e1) { throw new GeneratorException("Error: Compiler had no main method"); } int exitCode = 0; // Backup std channels PrintStream stdErr = System.err; PrintStream stdOut = System.out; // Local channels StringOutputStream err = new StringOutputStream(); StringOutputStream out = new StringOutputStream(); System.setErr(new PrintStream(err)); System.setOut(new PrintStream(out)); try { Object retVal = (Object) compilerMainMethod.invoke(compilerClass,new Object[]{ arguments }); if (retVal != null &amp;amp;amp;&amp;amp;amp; retVal instanceof Integer) exitCode = ((Integer) retVal).intValue(); } catch (InvocationTargetException e) { throw new GeneratorException(e); } catch (Throwable e) { e.printStackTrace(); throw new GeneratorException("IDL compilation failed"); } finally { if (!"".equals(out.toString())) logger.log(out.toString()); if (!"".equals(err.toString())) logger.log(err.toString()); // Restore std channels System.setErr(stdErr); System.setOut(stdOut); } } } [/sourcecode]
EOF ( End Of Fun )
Özcan Acar

Subclipse Şifresi

Elipse altında Subversion pluginini kullanıyorsanız, bir Subversion repositorisine bağlanmak için kullandığınız şifreyi nasıl değiştirebileceğinizi merak etmiş olabilirsiniz. Eclipse altında bu şifreyi değiştirmek mümkün değil, çünkü plugin şifreyi %APPDATA%\Subversion\auth (Linux altında ~/.subversion/auth) altında saklıyor. Bu dizini sildiğiniz taktirde, tekrar şifreyi girme panelini görebilirsiniz.

Maven2 ve OutOfMemory

Danışman olarak çalıştığım bir projenin modüllerini Maven2 kullanacak şekilde yeniden yapılandırdım. Birçok JAR ve WAR dosyası bir araya gelerek büyük bir EAR dosyası oluşturdu. JAR ve WAR’ları oluştururken bir sorun yaşamadım, lakin EAR dosyasını alırken durum farkli idi. EAR projesi için maven clean install komutunu kullandığımda aşağıdaki hata oluşuyordu:

[sourcecode language=”java”]
java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Arrays.java:2882)
at java.lang.AbstractStringBuilder.expandCapacity(AbstractStringBuilder.java:100)
at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:515)
at java.lang.StringBuffer.append(StringBuffer.java:306)
at java.io.StringWriter.write(StringWriter.java:77)
at hidden.org.codehaus.plexus.util.IOUtil.copy(IOUtil.java:214)
at hidden.org.codehaus.plexus.util.IOUtil.toString(IOUtil.java:416)
at hidden.org.codehaus.plexus.util.IOUtil.toString(IOUtil.java:405)
at org.apache.maven.project.DefaultMavenProjectBuilder.readModel(DefaultMavenProjectBuilder.java:1595)
at org.apache.maven.project.DefaultMavenProjectBuilder.readModel(DefaultMavenProjectBuilder.java:1571)
at org.apache.maven.project.DefaultMavenProjectBuilder.findModelFromRepository(DefaultMavenProjectBuilder.java:562)
at org.apache.maven.project.DefaultMavenProjectBuilder.buildFromRepository(DefaultMavenProjectBuilder.java:251)
at org.apache.maven.project.artifact.MavenMetadataSource.retrieveRelocatedProject(MavenMetadataSource.java:163)
at org.apache.maven.project.artifact.MavenMetadataSource.retrieveRelocatedArtifact(MavenMetadataSource.java:94)
at org.apache.maven.artifact.resolver.DefaultArtifactCollector.recurse(DefaultArtifactCollector.java:387)
at org.apache.maven.artifact.resolver.DefaultArtifactCollector.recurse(DefaultArtifactCollector.java:435)
at org.apache.maven.artifact.resolver.DefaultArtifactCollector.recurse(DefaultArtifactCollector.java:435)
at org.apache.maven.artifact.resolver.DefaultArtifactCollector.collect(DefaultArtifactCollector.java:74)
at org.apache.maven.artifact.resolver.DefaultArtifactResolver.resolveTransitively(DefaultArtifactResolver.java:316)
at org.apache.maven.artifact.resolver.DefaultArtifactResolver.resolveTransitively(DefaultArtifactResolver.java:304)
at org.apache.maven.plugin.DefaultPluginManager.resolveTransitiveDependencies(DefaultPluginManager.java:1499)
at org.apache.maven.plugin.DefaultPluginManager.executeMojo(DefaultPluginManager.java:442)
at org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeGoals(DefaultLifecycleExecutor.java:694)
at org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeGoalWithLifecycle(DefaultLifecycleExecutor.java:556)
at org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeGoal(DefaultLifecycleExecutor.java:535)
at org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeGoalAndHandleFailures(DefaultLifecycleExecutor.java:387)
at org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeTaskSegments(DefaultLifecycleExecutor.java:348)
at org.apache.maven.lifecycle.DefaultLifecycleExecutor.execute(DefaultLifecycleExecutor.java:180)
at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:328)
at org.apache.maven.DefaultMaven.execute(DefaultMaven.java:138)
at org.apache.maven.cli.MavenCli.main(MavenCli.java:362)
at org.apache.maven.cli.compat.CompatibleMain.main(CompatibleMain.java:60)
[INFO] ————————————————————————
[INFO] Total time: 3 seconds
[INFO] Finished at: Tue Oct 26 11:40:45 EEST 2010
[INFO] Final Memory: 9M/9M
[INFO] ————————————————————————
C:\CCVIEWS\itcs_oacar_UCM_YUKSELIS\CBVOB01\CorebankingAppBuilder>
[/sourcecode]

EAR dosyası birçok JAR ve WAR’dan oluştuğu için doğal olarak büyük bir hacme sahip. Default Maven2 ayarları bu boyuttaki bir EAR dosyasını oluşturmak için yeterli değil.

Bu sorunu aşmak için Maven’in daha fazla hafıza alanını kullanabilmesi gerekiyor. Bunu sağlamak için Windows işletim sistem ile çalışıyorsanız, console altında

set MAVEN_OPTS=-Xmx512m

ya da Linux/Unix altında

export MAVEN_OPTS=-Xmx512m

komutunu girmeniz yeterli. Bir sonraki mvn komutu tanımladığınız şekilde yeni hafıza alanı ile çalşacaktır.

Covariant Return Types

Java 1.5 ile kullanıma sunulan ve covariant return type ismini taşıyan bir yapı mevcuttur. Java 1.5 öncesi bir alt sınıf, bir üst sınıfın sahip olduğu metodu yeniden implemente etmek (method overriding) istediginde, bu metodun geri döndürdüğü veri tipinin, üst sınıftaki metot ile aynı olması gerekmekteydi. Bunun bir örneği aşağıda yer almaktadır.

[source language=’java’]
package test.controller;

import java.util.Map;

import test.criteria.Criteria;

public abstract class AbstractController implements Controller
{
public void executeFilter()
{
Map map = createCriteria().getCriteriaMap();

}

public abstract Criteria createCriteria();
}
[/source]

Soyut (abstract) olan AbstractController sınıfı bünyesinde, alt sınıflarda implemente edilmek üzere createCriteria() isminde bir metot tanımlıyoruz. AccountTableController sınıfı bu metodu implemente etmektedir.

[source language=’java’]
package test.controller;

import java.util.HashMap;
import java.util.Map;

import test.criteria.MyCriteria;
import test.model.Konto;

public class AccountTableController extends AbstractController
{
private Account model = new Account();

public Criteria createCriteria()
{
Map map = new HashMap();
map.put(“kontonr”, model.getKontonr());
map.put(“blz”, model.getBlz());
return new Criteria(map);
}
}
[/source]

Görüldüğü gibi AccountTableController.createCriteria() metodu Criteria tipinde bir degeri geri döndürmektedir. Bu metodun geri döndürdüğü değerin Java 1.5 öncesi Criteria tipinde olması gerekmektedir, çünkü üst sınıfta tanımlanmış olan bu metodun, üst sınıf bünyesinde geri döndürdüğü değer Criteria tipindedir.

Java 1.5 ile bu durum değişmiştir. Üst sınıfta yer alan bir metot alt sınıfta tekrar reimplement edilirse (method overriding), reimplemente edilen metodun geri döndürdüğü değerin tipi, üst sınıftaki metodun geri döndürdüğü sınıfın bir alt sınıfı olabilir. Bunun bir örneği aşağıda yer almaktadır.

[source language=’java’]
package test.controller;

import java.util.HashMap;
import java.util.Map;

import test.criteria.MyCriteria;
import test.model.Konto;

public class AccountTableController extends AbstractController
{
private Account model = new Account();

@Override
public MyCriteria createCriteria()
{
Map map = new HashMap();
map.put(“kontonr”, model.getKontonr());
map.put(“blz”, model.getBlz());
return new MyCriteria(map);
}
}
[/source]

MyCriteria, Criteria sınıfının bir alt sınıfıdır ve AccountTableController sınıfı implemente ettiği createCriteria() metodunun MyCriteria tipinde bir değeri geri döndürmesi sağlamıştır. Bu şekilde cast işlemi yapılmadan alt sınıfların daha zengin tipte verileri geri döndürebilmeleri amaçlanmaktadır.


EOF ( End Of Fun)
Özcan Acar

DBUnit JUnit Entegrasyonu

DBUnit JUnit ile entegre edilerek entegrasyon ve regresyon testlerinin JUnit testleri olarak implemente edilmesi mümkündür. DBUnit JUnit entegrasyonu aşağıdaki şekilde gerçekleştirilebilir. Bilgibankası olarak örnekte HSQLDB kullanılmıştır (bakınız HSQLDB JUnit entegrasyonu). Verilerin dbunit-dataset.xml dosyasında tanımlanmıs olması gerekmektedir. setUp() metodu her test öncesi JUnit frameworkü tarafından koşturulacağı için istenilen veriler test öncesi bilgibankasına eklenmiş ve regresyon testleri için taban oluşturulmuş olacaktır.

[source language=’java’]

// JDBC Driver
public static final String DRIVERCLASS = “org.hsqldb.jdbcDriver”;
// standalone mode
public static final String CONNECTIONURL = “jdbc:hsqldb:hsql://localhost:9006/mydb”;
public static final String USERNAME = “sa”;
public static final String PASSWORD = “”;

public void setUp() throws Exception
{
PreparedStatement pstmt = null;

try
{
IDatabaseConnection connection = getConnection();
IDataSet dataSet = getDataSet();
try
{
// REF INTEGRITY MUST BE DISABLED IN ORDER TO IMPORT THE DATA
// THIS SOLUTION IS ONLY VALID FOR HSQLDB 1.8
pstmt = connection.getConnection().prepareStatement(“set referential_integrity FALSE”);
pstmt.executeUpdate();
DatabaseOperation.CLEAN_INSERT.execute(connection, dataSet);
pstmt = connection.getConnection().prepareStatement(“set referential_integrity TRUE”);
pstmt.executeUpdate();
}
finally
{
connection.close();
}
}
catch (Exception e)
{
throw e;
}
}

protected IDatabaseConnection getConnection() throws Exception
{
Class.forName(DRIVERCLASS);
Connection jdbcConnection = DriverManager.getConnection(CONNECTIONURL, USERNAME, PASSWORD);
IDatabaseConnection con = new DatabaseConnection(jdbcConnection);
DatabaseConfig config = con.getConfig();
config.setProperty(DatabaseConfig.PROPERTY_DATATYPE_FACTORY, new HsqldbDataTypeFactory());
return con;
}

protected final IDataSet getDataSet() throws Exception
{
final URL url = DatabaseTestCase.class.getResource( “/dbunit-dataset.xml”);
final File file = new File(url.getPath());
return new FlatXmlDataSet(file);
}
[/source]

HSQLDB ve JUnit Entegrasyonu

Oluşturduğum entegrasyon JUnit testlerinde, testler öncesi verilerin bilgibankasına aktarılması gerekiyor. Aşağıda yer aldığı şekilde JUnit sınıfı bünyesinde HSQLDB serveri standalone modda çalıştırılabilir ve DBUnit ile veriler bilgibankasına eklenebilir.

[source language=’java’]

// JDBC Driver
public static final String DRIVERCLASS = “org.hsqldb.jdbcDriver”;
// standalone mode
public static final String CONNECTIONURL = “jdbc:hsqldb:hsql://localhost:9006/mydb”;

public static final String USERNAME = “sa”;
public static final String PASSWORD = “”;

private void startHsqlDb()
{

if(!isHsqlDbStarted())
{
Server hsqlServer = new Server();
hsqlServer.setAddress(“localhost”);
hsqlServer.setPort(9006);
hsqlServer.setDatabasePath(0, “db/hsqldb-data/”);
hsqlServer.setDatabaseName(0, “mydb”);
hsqlServer.start();
}
}

private boolean isHsqlDbStarted()
{
boolean result = false;
try
{
Class.forName(DRIVERCLASS);
Connection con = DriverManager.getConnection(CONNECTIONURL, USERNAME, PASSWORD);
con.close();
result = true;
}
catch (Exception e)
{
e.printStackTrace();
}
return result;
}

private static void stopHsqlDb()
{
try
{
Class.forName(DRIVERCLASS);
Connection con = DriverManager.getConnection(CONNECTIONURL, USERNAME, PASSWORD);
Statement statement = con.createStatement();
statement.execute(“SHUTDOWN”);
statement.close();
con.close();
}
catch (Exception e)
{
e.printStackTrace();
}
}
[/source]

JPA Anotasyonları ve Dinamik Tablo İsmi

Projelerde komponent tabanlı çalışmaya özen gösteriyorum. Komponent olarak geliştirdiğim bir modülü, konfigürasyon değişikliği yaparak başka bir projede kullanabilmeliyim. Komponentler kodun tekrar kullanımını (reuse) ve programcının daha az kod yazmasını mümkün kılar.

Komponent bünyelerinde Hibernate kullanarak, komponent verilerini bilgibankasında sakliyorum. Şu an geliştirdiğim JugTR.org projesinde JPA anotasyonlarını kullandım. Aşağıda blog kayıtlarını temsil eden Blog Entity sınıfı yer alıyor.

[source language=’java’]
package bt.common.manager.blog.domain;

import java.io.Serializable;
import java.sql.Timestamp;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name=”blog”)
public class Blog implements Serializable
{
private static final long serialVersionUID = 1L;

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = “id”)
private Long id;

@Column(name = “subject”, nullable = false)
private String subject;

@Column(name = “body”, nullable = false)
private String body;

@Column(name = “userid”, nullable = false)
private Long user;

@Column(name = “created”, nullable = false)
private Timestamp created;

public Blog(long blogId)
{
this.id = blogId;
}

public Blog()
{

}

public Long getId()
{
return id;
}

public void setId(Long id)
{
this.id = id;
}

public String getSubject()
{
return subject;
}

public void setSubject(String subject)
{
this.subject = subject;
}

public String getBody()
{
return body;
}

public void setBody(String body)
{
this.body = body;
}

public Long getUser()
{
return user;
}

public void setUser(Long user)
{
this.user = user;
}

public Timestamp getCreated()
{
return created;
}

public void setCreated(Timestamp created)
{
this.created = created;
}
}
[/source]

@Table anotasyonunu kullanarak bilgibankası tablosunu belirliyorum. Örnekte görüldüğü gibi Blog nesnesinin ihtiva ettiği veriler blog isimli tabloda saklanmakta.

Blog komponentini (Blog.java ve diger komponent sınıfları) daha önce başka projelerde de kullandım. Komponenti tekrar kullanmak (reuse) benim için kod üzerinde değişiklik yapmamak anlamına gelmektedir. Eğer blog verilerini jugtr_blog isimli tabloda tutmak istersem, Blog.java sınıfında yer alan @Table anotasyonunu değiştirmem gerekiyor. Bu durumda komponentin kodunu değiştirmiş olacağım. Bu da bir nevi tekrar kullanım, lakin bu durumda komponentin birden fazla versiyonu oluşmakta ve bunların bakımı zamanla güçleşmektedir. Benim hayalimde kod üzerinde değişiklik yapmadan Blog komponenentini istediğim projede kullanabilmek ve bilgibankası tablo ismini projeye göre değiştirebilmek var. Bu ne yazik ki JPA anotasyonlarını kullanarak mümkün değil. Eğer @Table anotasyonu ile tablo ismini belirlediyseniz, o zaman tablo ismini değiştirmek için sınıfı değiştirmeniz gerekiyor.

Bu çoktan beri üzerinde çalıştığım bir sorun. Sorunu Hibernate bünyesinde bir interceptor sınıf oluşturarak çözebildim. Interceptor sınıfı aşağıdaki yapıya sahip:

[source language=’java’]
package bt.jugtr.application.common.hibernate;

import org.hibernate.EmptyInterceptor;

public class JugTRHibernateInterceptor extends EmptyInterceptor
{

private static final long serialVersionUID = 1L;

@Override
public String onPrepareStatement(String sql)
{
String prepedStatement = super.onPrepareStatement(sql);

prepedStatement = prepedStatement.replaceAll(“public.”, “jugtr.”);

return prepedStatement;
}
}
[/source]

Bu interceptor sınıfını aktive etmek için SessionFactory konfigürasyonunu (Spring applicationContext.xml bünyesinde) aşağıdaki şekilde değiştiriyorum:

[source language=’java’]
org.hibernate.cfg.AnnotationConfiguration ${hibernate.dialect} true true true after_transaction thread bt.common.manager.user.domain.BTUser
bt.common.manager.user.domain.Authorities
bt.common.manager.blog.domain.Blog
bt.common.manager.blog.domain.BlogComment


[/source]

Hibernate, oluşturduğu SQL komutunu bilgibankasına göndermeden önce JugTRHibernateInterceptor sınıfının onPrepareStatement() metodunu koşturur. Bu metot bünyesinde public. ile başlayan tablo isimlerini jugtr. olarak değiştiriyorum. public. ve jugtr. bilgibankasında kullandığım şemalar (schema). Hibernate tarafından oluşturulan SQL komutlarında public. isminin kullanımını @Table anotasyonuna schema değişkenini ekleyerek sağlayabilirim:

[source language=’java’]
@Entity
@Table(name=”blog”, schema=”public”)
public class Blog implements Serializable
{
}
[/source]

Bu değişikliğin ardından Hibernate tarafından oluşturulan bir SQL aşağıdaki şekilde olacaktır:

[source language=’java’]
/* from Blog order by id desc */
select top ? blog0_.id as id2_, blog0_.body as body2_, blog0_.created as created2_, blog0_.subject
as subject2_, blog0_.userid as userid2_ from public.blog blog0_ order by blog0_.id desc
[/source]

Görüldüğü gibi Hibernate blog tablosunu public.blog seklinde adreslemeye başladı. Tekrar JugTRHibernateInterceptor sınıfına geri dönelim. onPrepareStatement() metodu bünyesinde SQL komutunda yer alan public. kelimesinin jugtr. olarak değiştirilmesini sağlıyorum. Bu şekilde dinamik olarak başka bir şemayı ve bu şemada bulunan blog tablosunu adreslemiş oluyorum. JugTRHibernateInterceptor devreye girdikten sonra SQL komutu aşağıdaki şekilde değişikliğe uğruyor. Bu şekilde başka bir şema içinde bulunan blog isimli tablo kullanımı mümkün oluyor. Görüldüğü gibi Blog.java kodunu değiştirmek zorunda kalmadan başka bir tabloyu kullanabildik.

[source language=’java’]
/* from Blog order by id desc */
select top ? blog0_.id as id2_, blog0_.body as body2_, blog0_.created as created2_, blog0_.subject
as subject2_, blog0_.userid as userid2_ from jugtr.blog blog0_ order by blog0_.id desc
[/source]


EOF ( End Of Fun)
Özcan Acar

Apache ile Tomcat Arasında Reverse Proxy Oluşturma

JugTR.org projesi Tomcat içinde deploy edilen bir Java 6 web aplikasyonu (Servlet 2.5 spec).  Bu aplikasyona http://www.jugtr.org adresi üzerinden ulaşabilmek için Tomcat’in 80 numaralı port üzerinde çalışması gerekmektedir. Kullandığım server üzerinde 80 numaralı portta Apache çalışmakta. Bu durumda Tomcat’i 80 numaralı port üzerinde çalıştırmam mümkün değil.  80 haricinde herhangi bir port seçerek, JugTR.org aplikasyonunu deploy edebilirim, örneğin port 8181. Bu durumda aplikasyonun erişim adresi http://www.jugtr.org:8181 olacaktır.

Apache ile Tomcat arasında Reverse Proxy oluşturarak JugTR.org aplikasyonuna 80 numaralı port üzerinden ulaşabiliriz. Reverse Proxy kullanıldığında http://www.jugtr.org adresine gelen kullanıcı istekleri Apache tarafından http://127.0.0.1:8181 adresine yönlendirilir ve bu şekilde 8181 portu üzerinde faaliyet gösteren Tomcat’e 80 numaralı port üzerinden erişim sağlanmış olur. Apache,  kullanıcı ile Tomcat içinde deploy edilmiş ve 8181 numaralı portta çalışan JugTR.org aplikasyonu arasında aracılık etmiş olur.

Apache ile Tomcat arasında Reverse Proxy oluşturmak için öncelikle mod_proxy modülünü Apache httpd.conf dosyasina eklememiz gerekiyor. Ben Apache 2 versiyonunu kullanıyorum. Aşağıda httpd.conf dosyasında yer alması gereken konfigürasyon bulunmakta.

 

[source language=’java’]

LoadModule proxy_module modules/mod_proxy.so

[Proxy *]
Order deny,allow
Allow from all
[/Proxy]

NameVirtualHost 192.168.1.76

[VirtualHost 192.168.1.76]
ServerName www.jugtr.org
ServerAlias www.jugtr.org jugtr.org
ServerAdmin info@jugtr.org
DocumentRoot /database/usr/local/apache/htdocs/
ProxyPass / http://127.0.0.1:8181/
[Location /]
ProxyPassReverse /
[/Location]
[/VirtualHost]

[/source]

Yukarda görüldügü gibi VirtualHost direktifini kullanarak, jugtr.org icin yeni bir virtual domain oluşturuyorum. Bu sayede aynı Apache kurulumu üzerinde birden fazla domain ismini kullanmam mümkün.

VirtualHost direktifini yakından incelediğimizde ProxyPass direktifi ile http://www.jugtr.org/ adresine gelen tüm kullanıcı isteklerinin (request) http://127.0.0.1:8181 adresine yönlendirildiğini görmekteyiz. ProxyPass direktifini kullanarak Apache ve Tomcat arasında Reverse Proxy oluşturmus oluyoruz.

Tomcat kullanıcı isteklerine konfigüre edildigi IP ve port adresi ile cevap verir. Yukardaki örnekte kullanıcı http://127.0.0.1:8181 adresine yönlendirilecektir. Bu durumda Apache ile Tomcat arasındaki proxy çalışmamaktadır. Tomcat’in gönderdigi cevapların reverse proxy adresinde olmasını sağlamak için Tomcat’in server.xml konfigürasyon dosyası üzerinde değişiklik yapmamız gerekmektedir. Yapılması gereken değişiklik Connector elementi bünyesindedir.

[source language=’java’]

Connector port=”1224″ maxThreads=”150″ minSpareThreads=”25″ maxSpareThreads=”75″ enableLookups=”false” redirectPort=”8443″

debug=”0″ acceptCount=”100″ connectionTimeout=”20000″ disableUploadTimeout=”true” URIEncoding=”UTF-8″ proxyName=”www.jugtr.org” proxyPort=”80″

[/source]

Connector elementinde kullandığımız proxyName ve proxyPort atributlarıyla Tomcat’in gönderdiği cevapların http://www.jugtr.org şeklinde olmasını sagladık.

 

 

EOF ( End Of Fun )
Özcan Acar

 

 

 

Embedded Jetty

Üzerinde çalıştığım açık kaynaklı JStorage projesinde Tomcat yerine, bir Java sınıfından koşturulabilecek Jetty containeri kullandım. Jetty, Tomcat gibi bir JSP/Servlet containeri. Aşağıdaki örnekte yer aldığı gibi Jetty bir Java (main()) program tarafından çalıştırilabilir.

[source language=’java’]
package org.jstorage.tracker.impl.restfull;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jstorage.tracker.TrackerException;
import org.jstorage.tracker.impl.AbstractTracker;
import org.mortbay.jetty.Connector;
import org.mortbay.jetty.Server;
import org.mortbay.jetty.bio.SocketConnector;
import org.mortbay.jetty.servlet.Context;
import org.mortbay.jetty.servlet.ServletHandler;
import org.mortbay.jetty.servlet.ServletHolder;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Scope;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.stereotype.Component;
import org.springframework.web.context.ContextLoaderListener;
import org.springframework.web.servlet.DispatcherServlet;

@Component(“RestfullTracker”)
@Scope(“singleton”)
public class RestfulTrackerImpl extends AbstractTracker
{

private final Log logger = LogFactory.getLog(RestfulTrackerImpl.class);

private Server server;
ApplicationContext applicationContext;
private int port;
private String ip;

public RestfulTrackerImpl()
{

}

protected void finalize() throws Throwable
{
super.finalize(); // not necessary if extending Object.
logger.info(“Stopping storage node with jetty”);
server.stop();
}

@Override
public void runInProductionMode()
{
try
{
logger.info(“Starting storage node with jetty in PRODUCTION MODE”);
server = new Server();
Connector connector = new SocketConnector();
connector.setPort(getPort());
server.setConnectors(new Connector[] { connector });

ServletHandler handler = new ServletHandler();
server.setHandler(handler);

handler.addServletWithMapping(“org.jstorage.tracker.impl.http.TrackerServlet”,”/”);

server.start();

server.join();
}
catch (Exception e)
{
throw new TrackerException(e);
}
}

@Override
public void runInTestMode()
{
try
{
Server server = new Server();
Connector connector = new SocketConnector();
connector.setPort(getPort());
server.setConnectors(new Connector[] { connector });

Context context = new Context(server, “/”, Context.SESSIONS);

ServletHolder servletHolder = new ServletHolder(new DispatcherServlet());
servletHolder.setInitParameter(“contextConfigLocation”,”classpath:spring-restfulltracker.xml”);
servletHolder.setInitOrder(1);
context.addServlet(servletHolder, “/*”);

// Add the Spring context listener
context.getInitParams().put(“contextConfigLocation”,”classpath:spring-restfulltracker.xml”);
ContextLoaderListener listener = new ContextLoaderListener();
context.addEventListener(listener);

server.start();
context.getServletHandler().initialize();

}
catch (Exception e)
{
throw new TrackerException(e);
}
}

public ApplicationContext getApplicationContext()
{
if(applicationContext == null)
{
applicationContext = new ClassPathXmlApplicationContext(“spring-restfulltracker.xml”);
}
return this.applicationContext;
}

public int getPort()
{
int p = this.port;

try
{
p = Integer.parseInt(System.getProperty(“port”));
}
catch (Exception e)
{
//ignore
}

return p;
}

public void setPort(int port) {
this.port = port;
}

public String getIp()
{
String ip = “127.0.0.1”;

try
{
ip = (String)System.getProperty(“ip”);
}
catch (Exception e)
{
//ignore
}
return ip;
}

public void setIp(String ip)
{
this.ip = ip;
}
}

[/source]

59. satırda org.jstorage.tracker.impl.http.TrackerServlet isimli Servlet sınıfını http://127.0.0.1/ adresinde erişilecek şekilde sisteme ekliyorum. Bu mapping işlemini genelde web aplikasyonlarında web.xml bünyesinde aşağıdaki şekilde gerçekleştiririz.

[source language=’java’]

ForwardedServlet
tests.Filter.ForwardedServlet


ForwardedServlet
/ForwardedServlet

[/source]

Yukarda yer alan programı çalıştırdıktan sonra Web tarayıcısı üzerinden http://127.0.0.1/ adresine bağlandığımız taktirde TrackerServlet.doGet() metodu işlem görecektir. -Dport=8080 şeklinde bir sistem değişkeni kullanarak (118. satır), Jetty’nin çalıştırılacağı portu değiştirebiliriz.


EOF (End of Fun)
Özcan Acar

JAXB ile Java-XML Mapping

JAXB (Java Xml Binding – https://jaxb.dev.java.net/) frameworkü kullanılarak Xml–>Java, Java–>Xml mapping işlemlerini gerçekleştirmek mümkündür. Bu yazılımcıya, Xml dosyalarının ihtiva ettiği verileri Java sınıflarıyla modelleme imkanı sunmakta ve verilerın işlenmesini kolaylaştırmaktadır. JAXB’nın nasıl kullanılabileceğini bir örnek üzerinde sizlere aktarmak istiyorum.

Aşağıda bir arabayı modelleyen bir DTD dosyası yer almaktadır.

[source language=’java’]







[/source]

Bu DTD dosyasını kullanarak JAXB yardımı ile Java mapping sınıfları oluşturabiliriz. Bu işlemi Ant ile yapmak mümkündür.

[source language=’java’]






[/source]

jaxb.home isminde bir değişken ile JAXB paketinin ihtiva ettiği Jar dosyalarının yerini tanımlıyoruz. Xml den türetilen Java sınıflarını oluşturabilmek için com.sun.tools.xjc.XJCTask sınıfını kullanıyoruz.

DTD dosyasından Java sınıflarını oluşturabilmemiz için tasit.jaxb isminde bir konfigürasyon dosyası oluşturmamız gerekmektedir. Bu dosyanın içeriği aşağıdaki şekildedir:

[source language=’java’]




[/source]

Java sınıflarını oluşturmak için Ant dosyasında tanımladığımız tasit isimli hedefi (target) koşturuyoruz. tasit hedefinde belirttiğimiz gibi Java sınıfları xjc tarafından oluşturulduktan sonra gen-src dizine yerleştirileceklerdir.

JAXB tarafından oluşturulan sınıflar aşağıda yer almaktadır.

[source language=’java’]
package com.tasit.mapping;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;

/**
*
*/
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = “”, propOrder = {
“model”,
“motor”
})
@XmlRootElement(name = “araba”)
public class Araba {

@XmlElement(required = true)
protected String model;
@XmlElement(required = true)
protected Motor motor;

/**
* Gets the value of the model property.
*
* @return
* possible object is
* {@link String }
*
*/
public String getModel() {
return model;
}

/**
* Sets the value of the model property.
*
* @param value
* allowed object is
* {@link String }
*
*/
public void setModel(String value) {
this.model = value;
}

/**
* Gets the value of the motor property.
*
* @return
* possible object is
* {@link Motor }
*
*/
public Motor getMotor() {
return motor;
}

/**
* Sets the value of the motor property.
*
* @param value
* allowed object is
* {@link Motor }
*
*/
public void setMotor(Motor value) {
this.motor = value;
}

}

[/source]

[source language=’java’]
package com.tasit.mapping;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;

/**
*
*/
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = “”, propOrder = {
“beygirgucu”,
“tipi”
})
@XmlRootElement(name = “motor”)
public class Motor {

@XmlElement(required = true)
protected String beygirgucu;
@XmlElement(required = true)
protected String tipi;

/**
* Gets the value of the beygirgucu property.
*
* @return
* possible object is
* {@link String }
*
*/
public String getBeygirgucu() {
return beygirgucu;
}

/**
* Sets the value of the beygirgucu property.
*
* @param value
* allowed object is
* {@link String }
*
*/
public void setBeygirgucu(String value) {
this.beygirgucu = value;
}

/**
* Gets the value of the tipi property.
*
* @return
* possible object is
* {@link String }
*
*/
public String getTipi() {
return tipi;
}

/**
* Sets the value of the tipi property.
*
* @param value
* allowed object is
* {@link String }
*
*/
public void setTipi(String value) {
this.tipi = value;
}

}
[/source]

[source language=’java’]
package com.tasit.mapping;

import javax.xml.bind.annotation.XmlRegistry;

/**
* This object contains factory methods for each
* Java content interface and Java element interface
* generated in the com.tasit.mapping package.
*

An ObjectFactory allows you to programatically
* construct new instances of the Java representation
* for XML content. The Java representation of XML
* content can consist of schema derived interfaces
* and classes representing the binding of schema
* type definitions, element declarations and model
* groups. Factory methods for each of these are
* provided in this class.
*
*/
@XmlRegistry
public class ObjectFactory {

public ObjectFactory() {
}

/**
* Create an instance of {@link Motor }
*
*/
public Motor createMotor() {
return new Motor();
}

/**
* Create an instance of {@link Araba }
*
*/
public Araba createAraba() {
return new Araba();
}

/**
* Create an instance of {@link Tasit }
*
*/
public Tasit createTasit() {
return new Tasit();
}

}

[/source]

[source language=’java’]
package com.tasit.mapping;

import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;

/**
*
*/
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = “”, propOrder = {
“araba”
})
@XmlRootElement(name = “Tasit”)
public class Tasit {

protected List araba;

/**
* Gets the value of the araba property.
*
*

* This accessor method returns a reference to the live list,
* not a snapshot. Therefore any modification you make to the
* returned list will be present inside the JAXB object.
* This is why there is not a set method for the araba property.
*
*

* For example, to add a new item, do as follows:
*

* getAraba().add(newItem);
* 

*
*
*

* Objects of the following type(s) are allowed in the list
* {@link Araba }
*
*
*/
public List getAraba() {
if (araba == null) {
araba = new ArrayList();
}
return this.araba;
}

}

[/source]

Şimdi Tasit.dtd den türetilmiş bir Xml dosyasında yer alan verileri xjc tarafından oluşturulan Java sınıflarına nasıl yükleyebileceğimizi yakından inceleyelim. Tasit.dtd ye uyumlu bir Xml örneği aşağıda yer almaktadır:

[source language=’java’]



Ford Fiesta

75
benzinli


Opel Agila

70
benzinli



[/source]

Aşağıda yer alan program yardımı ile Xml dosyasında yer alan verileri elde edebiliriz:

[source language=’java’]

package com.tasit;

import java.io.File;
import java.io.FileReader;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.Unmarshaller;
import javax.xml.stream.EventFilter;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.events.StartElement;
import javax.xml.stream.events.XMLEvent;

import com.tasit.mapping.Araba;

public class Main {

/**
* @param args
*/
public static void main(String[] args) throws Exception
{
XMLInputFactory xmlif = XMLInputFactory.newInstance();
FileReader fr = new FileReader(new File(“c:/temp/araba.xml”));
XMLEventReader xmler = xmlif.createXMLEventReader(fr);
EventFilter filter = new EventFilter() {
public boolean accept(XMLEvent event) {
return event.isStartElement();
}
};

XMLEventReader xmlfer = xmlif.createFilteredReader(xmler, filter);
StartElement e = (StartElement) xmlfer.nextEvent();

JAXBContext ctx = JAXBContext.newInstance(“com.tasit.mapping”);
Unmarshaller um = ctx.createUnmarshaller();

while (xmlfer.peek() != null)
{
Object o = um.unmarshal(xmler);
if (o instanceof Araba)
{
Araba araba = (Araba) o;
System.out.println(araba.getModel());
System.out.println(araba.getMotor().getBeygirgucu());
System.out.println(araba.getMotor().getTipi());
System.out.println(“———————————–“);
}
}
fr.close();

}

}
[/source]

Ekran çıktısı şu şekilde olacaktır:

[source language=’java’]
Ford Fiesta
75
benzinli
———————————–
Opel Agila
70
benzinli
———————————–
[/source]

Xml–>Java mapping işlemlerinin nasıl yapıldığını gördük. Aşağıdaki Java örneği Java–>Xml mapping işleminin nasıl yapıldığını göstermektedir.

[source language=’java’]

package com.tasit;

import java.io.StringWriter;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import com.tasit.mapping.Araba;
import com.tasit.mapping.Motor;

public class Main {

/**
* @param args
*/
public static void main(String[] args) throws Exception
{

Araba araba = new Araba();
Motor motor = new Motor();
motor.setBeygirgucu(“300”);
motor.setTipi(“dizel”);
araba.setMotor(motor);
araba.setModel(“Crossfire”);

Marshaller marshaller = null;

marshaller = JAXBContext.newInstance(“com.tasit.mapping”).createMarshaller();
marshaller.setProperty( Marshaller.JAXB_FORMATTED_OUTPUT, true );
StringWriter writer = new StringWriter();

marshaller.marshal(araba, writer);

System.out.println(writer.getBuffer().toString());

}

}
[/source]

Programı koşturduğumuzda ekran çıktısı şu şekilde olacaktır:

[source language=’java’]


Crossfire

300
dizel

[/source]


EOF (End Of Fun)
Özcan Acar

Hibernate Session Problemi: Illegal attempt to associate a collection with two open sessions

BTSoru.com projesini modellerken aşağıdaki hata ile karşılaştım:

org.hibernate.HibernateException: Illegal attempt to associate a collection with two open sessions
at bt.btsoru.application.manager.question.facade.QuestionManagerImpl.addQuestion(QuestionManagerImpl.java:57)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)

Hatanın oluşmasını sağlayan kod bölümü şu şekilde:

[source language=’java’]
// Soruyu ekleme bölümü
BTUser user = new BTUser();
user.setId(WebSession.get().getUser().getId());
getQuestion().setUser(WebSession.get().getUser());

QuestionManagerResult result = qManager.addQuestion(getQuestion());

[/source]
Kod 1.1

Birinci satırda yer alan BTUser bir Hibernate nesnesi. BTUser ile bir kullanıcıyı modelliyorum. UML diagramı aşağıda yer almaktadır:

Sistem bünyesinde her kullanıcının sıfır ya da birden fazla sorusu olabilir. Sorulan her soru sadece bir kullanıcıya aittir. BTUser gibi BTQuestion sınıfı da bir Hibernate nesnesi, yani ihtiva ettiği veriler bilgilbankasında saklanabilmekte.

Kod 1.1 de yer alan kodun yedinci satırında qManager.addQuestion() metodu ile yeni bir soruyu (BTQuestion nesnesini) bilgibankasına ekliyorum. BTQuestion nesnesinin ihtiva ettiği verileri bir form üzerinden kullanıcı daha önce girdi. Gerekli validasyon işlemlerini daha önce gerçekleştirerek, BTQuestion nesnesini, yani kullanıcının sisteme eklemek istediği soruyu bilgibankasına ekliyorum.

BTUser ile BTQuestion arasındaki ilişkiyi sağlamak için aşağıdaki şekilde BTQuestion bünyesinde bir ManyToOne ilişkisini oluşturuyorum. Bu şekilde BTQuestion nesnesi bir kullanıcıya atanmış olacaktır.

[source language=’java’]

@ManyToOne(cascade=CascadeType.ALL,fetch=FetchType.LAZY)
@JoinTable(
name=”btsoru_user_questions”,
joinColumns = { @JoinColumn( name=”question_id”, unique = false) },
inverseJoinColumns = @JoinColumn( name=”user_id”, unique = false)
)
private BTUser user;
[/source]
Kod 1.2

@JoinTable anotasyonu ile kullanıcı ile sahip olduğu soruları ilişkilendirmek için yeni bir bilgibankası tablosu oluşturuyorum (btsoru_user_question). Bu tablo bünyesinde kullanıcının sahip olduğu tekil numara (user_id) ve soru nesnesinin tekil numarası (question_id) yer almaktadır. Bu şekilde kullanıcı ve soruyu ilişkilendirmiş oluyorum.

Bu konfigurasyonda kod 1.1 altıncı satırda yer alan qManager.addQuestion() metodunu kullandığım taktirde, yukarda bahsettiğim Hibernate problemi oluşuyor. Bunun sebebi şudur: Kod 1.1, üçüncü satırda Hibernate tarafından kontrol edilen bir sınıfdan (BTUser) bir nesne oluşturarak, bu nesneye id değerini atıyorum. Buradaki amacım, bir BTQuestion nesnesini bilgibankasına eklemeden önce bir BTUser nesnesi ile ilişkilendirmek. Bunu getQuestion().setUser(WebSession.get().getUser()); ile gerçekleştiriyorum. Bunu yapmadığın taktirde, bilgibankasına herhangi kullanıcıya ait olmayan bir soru yer alacaktır ve bu istenmeyen bir durumdur. Dördüncü satırda WebSession.get().getUser().getId()); ile kullanıcı id sini session içinde bulunan BTUser nesnesinden ediniyorum. Id değişkenini kullanıcının kimlik numarası olarak düşünebilirsiniz. Bu işlemi gerçekleştirebilmem için kullanıcının soruyu sisteme eklemeden önce isim ve şifresi ile login yapmış olması gerekmektedir. Login işlemi ardından oluşturduğum BTUser nesnesini session içine yerleştiriyorum ve gerekli durumlarda WebSession.get().getUser()); ile bu nesneye, yani login yapmış kullanıcının bilgilerine ulaşıyorum. Sorun da ne yazik ki burada oluşmaktadır. Login işleminin ardından belirli bir id ye sahip bir BTUser nesnesi sistemde (session içinde) mevcuttur. Aynı id ye sahip ikinci bir nesne oluşturulduğu taktirde, Hibernate buna Illegal attempt to associate a collection with two open sessions hata mesaji ile karşı çıkacaktır. Böyle bir konstellasyonda Hibernate kontrolünde bulunan (Hibernate Cache) bir nesneye rakip olacak aynı id yi taşıyan ikinci bir nesne oluşturup kullanmamız mümkün değildir.

Bu sorunu aşmak için BTQuestion bünyesinde bulunan BTUser değişkeninin mapping parametrelerini kod 1.3 deki gibi değiştirmemiz yeterli olacaktır.

[source language=’java’]

@ManyToOne(fetch=FetchType.LAZY)
@JoinTable(
name=”btsoru_user_questions”,
joinColumns = { @JoinColumn( name=”question_id”, unique = false) },
inverseJoinColumns = @JoinColumn( name=”user_id”, unique = false)
)
private BTUser user;
[/source]
Kod 1.3

@ManyToOne anotasyonundaki cascade=CascadeType.ALL elementini kaldırmamız yeterlidir.


EOF (End of Fun)
Özcan Acar

Eclipse ile Java EE Ear Projesi

Java EE 5 aplikasyonları EAR (Enterprise Archive) arşiv dosyaları içinde yer alır. Bir EAR dosyası JAR (Java Archive) dosyası yapısına ve .ear dosya ekine sahiptir. EAR dosyası içinde bir Java EE projesini oluşturan diğer modül arşivleri yer alır. Bir sonraki resimde bir EAR arşiv dosyasının yapısı yer almaktadır.

 

Bir EAR arşivinde yer alabilecek modül arşivleri şunlardır:

  • EJB JAR: EJB komponentlerin yer aldığı JAR dosyasıdır. Bu dosya içinde sadece EJB implementasyonları yer alır.
  • EJB Client JAR: EJB komponentlerinin sahip oldukları Remote ve Local EJB interface sınıflar EJB Client JAR arşivlerinde yer alır. Bu JAR dosyası EJB komponentlerini kullanmak isteyen diger modüllerin kullanımına sunulur.
  • WAR: Web aplikasyonları WAR (Web Application Archive) arşiv dosyalarında yer alır.

Bu yazıyı PDF olarak edinebilirsiniz.

  Eclipse ile EJB 3 EAR Arşiv Projesi (760,4 KiB, 10.431 yükleme)

Eclipse projesini .rar dosyasi olarak  edinebilirsiniz.

workspace_emarket.zip